diff --git a/.gitignore b/.gitignore index b569ed9..a6ce461 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /.github /build /.cache -.clang-format \ No newline at end of file +.clang-format +/map/src/std.hpp \ No newline at end of file diff --git a/map/src/map.hpp b/map/src/map.hpp index 1f53e7b..4357ced 100644 --- a/map/src/map.hpp +++ b/map/src/map.hpp @@ -5,186 +5,466 @@ #define SJTU_MAP_HPP // only for std::less -#include +#include #include -#include "utility.hpp" +#include #include "exceptions.hpp" +#include "utility.hpp" namespace sjtu { -template< - class Key, - class T, - class Compare = std::less -> class map { -public: - /** - * the internal type of data. - * it should have a default constructor, a copy constructor. - * You can use sjtu::map as value_type by typedef. - */ - typedef pair value_type; - /** - * see BidirectionalIterator at CppReference for help. - * - * if there is anything wrong throw invalid_iterator. - * like it = map.begin(); --it; - * or it = map.end(); ++end(); - */ - class const_iterator; - class iterator { - private: - /** - * TODO add data members - * just add whatever you want. - */ - public: - iterator() { - // TODO - } - iterator(const iterator &other) { - // TODO - } - /** - * TODO iter++ - */ - iterator operator++(int) {} - /** - * TODO ++iter - */ - iterator & operator++() {} - /** - * TODO iter-- - */ - iterator operator--(int) {} - /** - * TODO --iter - */ - iterator & operator--() {} - /** - * a operator to check whether two iterators are same (pointing to the same memory). - */ - value_type & operator*() const {} - bool operator==(const iterator &rhs) const {} - bool operator==(const const_iterator &rhs) const {} - /** - * some other operator for iterator. - */ - bool operator!=(const iterator &rhs) const {} - bool operator!=(const const_iterator &rhs) const {} +template > +class map { + public: + /** + * the internal type of data. + * it should have a default constructor, a copy constructor. + * You can use sjtu::map as value_type by typedef. + */ + typedef pair value_type; - /** - * for the support of it->first. - * See for help. - */ - value_type* operator->() const noexcept {} - }; - class const_iterator { - // it should has similar member method as iterator. - // and it should be able to construct from an iterator. - private: - // data members. - public: - const_iterator() { - // TODO - } - const_iterator(const const_iterator &other) { - // TODO - } - const_iterator(const iterator &other) { - // TODO - } - // And other methods in iterator. - // And other methods in iterator. - // And other methods in iterator. - }; - /** - * TODO two constructors - */ - map() {} - map(const map &other) {} - /** - * TODO assignment operator - */ - map & operator=(const map &other) {} - /** - * TODO Destructors - */ - ~map() {} - /** - * TODO - * access specified element with bounds checking - * Returns a reference to the mapped value of the element with key equivalent to key. - * If no such element exists, an exception of type `index_out_of_bound' - */ - T & at(const Key &key) {} - const T & at(const Key &key) const {} - /** - * TODO - * access specified element - * Returns a reference to the value that is mapped to a key equivalent to key, - * performing an insertion if such key does not already exist. - */ - T & operator[](const Key &key) {} - /** - * behave like at() throw index_out_of_bound if such key does not exist. - */ - const T & operator[](const Key &key) const {} - /** - * return a iterator to the beginning - */ - iterator begin() {} - const_iterator cbegin() const {} - /** - * return a iterator to the end - * in fact, it returns past-the-end. - */ - iterator end() {} - const_iterator cend() const {} - /** - * checks whether the container is empty - * return true if empty, otherwise false. - */ - bool empty() const {} - /** - * returns the number of elements. - */ - size_t size() const {} - /** - * clears the contents - */ - void clear() {} - /** - * insert an element. - * return a pair, the first of the pair is - * the iterator to the new element (or the element that prevented the insertion), - * the second one is true if insert successfully, or false. - */ - pair insert(const value_type &value) {} - /** - * erase the element at pos. - * - * throw if pos pointed to a bad element (pos == this->end() || pos points an element out of this) - */ - void erase(iterator pos) {} - /** - * Returns the number of elements with key - * that compares equivalent to the specified argument, - * which is either 1 or 0 - * since this container does not allow duplicates. - * The default method of check the equivalence is !(a < b || b > a) - */ - size_t count(const Key &key) const {} - /** - * Finds an element with key equivalent to key. - * key value of the element to search for. - * Iterator to an element with key equivalent to key. - * If no such element is found, past-the-end (see end()) iterator is returned. - */ - iterator find(const Key &key) {} - const_iterator find(const Key &key) const {} + private: + static Compare comparer; + /** + * The NIL Node is recorded as a nullptr pointer. + */ + struct RedBlackTreeNodeType { + value_type val; + RedBlackTreeNodeType *left, *right, *parent; + enum RedBlackTreeColorType { RED, BLACK } color; + RedBlackTreeNodeType() : left(nullptr), right(nullptr), parent(nullptr), color(RED) {} + RedBlackTreeNodeType(const value_type &val, RedBlackTreeNodeType *left, RedBlackTreeNodeType *right, + RedBlackTreeNodeType *parent, RedBlackTreeColorType color) + : val(val), left(left), right(right), parent(parent), color(color) {} + inline RedBlackTreeNodeType *GetGrandParent() const noexcept { + if (parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + return parent->parent; + } + inline RedBlackTreeNodeType *GetUncle() const noexcept { + RedBlackTreeNodeType *grand_parent = GetGrandParent(); + if (grand_parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + if (parent == grand_parent->left) + return grand_parent->right; + else + return grand_parent->left; + } + inline RedBlackTreeNodeType *GetSibling() const noexcept { + if (parent == nullptr) +#if __cplusplus >= 202002L + [[unlikely]] +#endif + return nullptr; + if (this == parent->left) + return parent->right; + else + return parent->left; + } + inline RedBlackTreeNodeType *&GetSelfPath(RedBlackTreeNodeType *&tree_root) noexcept { + if (parent == nullptr) return tree_root; + if (this == parent->left) + return parent->left; + else + return parent->right; + } + inline void SetChildrensParent() noexcept { + if (left != nullptr) left->parent = this; + if (right != nullptr) right->parent = this; + } + inline void RotateLeft(RedBlackTreeNodeType *&tree_root) noexcept { + assert(this->right != nullptr); + RedBlackTreeNodeType *parent_backup = parent; + RedBlackTreeNodeType *&path = this->GetSelfPath(tree_root); + RedBlackTreeNodeType *replacement = this->right; + this->right = replacement->left; + replacement->left = this; + this->SetChildrensParent(); + replacement->SetChildrensParent(); + path = replacement; + replacement->parent = parent_backup; + } + inline void RotateRight(RedBlackTreeNodeType *&tree_root) noexcept { + assert(this->left != nullptr); + RedBlackTreeNodeType *parent_backup = parent; + RedBlackTreeNodeType *&path = this->GetSelfPath(tree_root); + RedBlackTreeNodeType *replacement = this->left; + this->left = replacement->right; + replacement->right = this; + this->SetChildrensParent(); + replacement->SetChildrensParent(); + path = replacement; + replacement->parent = parent_backup; + } + void InsertFixUp(RedBlackTreeNodeType *&tree_root) { + if (parent == nullptr) { + // Case 1 + color = RedBlackTreeColorType::BLACK; + return; + } + if (parent->color == RedBlackTreeColorType::BLACK) return; + if (parent->parent == nullptr) { + // Case 2 & 3 + parent->color = RedBlackTreeColorType::BLACK; + return; + } + RedBlackTreeNodeType *uncle = GetUncle(); + RedBlackTreeColorType *grand_parent = GetGrandParent(); + if (uncle != nullptr && uncle->color == RedBlackTreeColorType::RED) { + // Case 4 + parent->color = RedBlackTreeColorType::BLACK; + uncle->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + grand_parent->InsertFixUp(tree_root); + return; + } + if (grand_parent->left == parent) { + if (parent->right == this) { + parent->RotateLeft(tree_root); + assert(parent->parent == this); + parent->InsertFixUp(tree_root); + return; + } + grand_parent->RotateRight(tree_root); + assert(grand_parent->parent == parent); + parent->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + } else { + if (parent->left == this) { + parent->RotateRight(tree_root); + assert(parent->parent == this); + parent->InsertFixUp(tree_root); + return; + } + grand_parent->RotateLeft(tree_root); + assert(grand_parent->parent == parent); + parent->color = RedBlackTreeColorType::BLACK; + grand_parent->color = RedBlackTreeColorType::RED; + } + } + /** + * @brief Insert a new node into the tree. + * + * @details This function will insert a new node into the tree. If insert successfully, it will return true. + * + * @param tree_root The root of the tree. + * @param val The value to be inserted. + * @param allow_replacement Whether to allow replacement if the key already exists. + * + * @return Whether the insertion is successful. + * + * @note Note that tree_root is a reference to the root of the tree. This function will modify the tree_root if + * necessary. + */ + bool Insert(RedBlackTreeNodeType *&tree_root, const value_type &val, bool allow_replacement) { + if (comparer(val.first, this->val.first)) { + if (left == nullptr) { + left = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); + left->parent = this; + left->InsertFixUp(tree_root); + } else { + return left->Insert(tree_root, val, allow_replacement); + } + } else if (comparer(this->val.first, val.first)) { + if (right == nullptr) { + right = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); + right->parent = this; + right->InsertFixUp(tree_root); + } else { + return right->Insert(tree_root, val, allow_replacement); + } + } else { + if (allow_replacement) { + this->val = val; + return true; + } + } + return false; + } + /** + * @brief The definition of ReleaseAll. + * + * @details This fuction will be called when the whole map is destructed. It will release all the memory allocated. + * + * @note Note that the node itself must be released outside this function. + */ + void ReleaseAll() { + if (left) left->ReleaseAll(); + if (right) right->ReleaseAll(); + delete left; + delete right; + } + }; + size_t node_count; + RedBlackTreeNodeType *tree_root; + + public: + /** + * see BidirectionalIterator at CppReference for help. + * + * if there is anything wrong throw invalid_iterator. + * like it = map.begin(); --it; + * or it = map.end(); ++end(); + */ + class const_iterator; + class iterator; + friend iterator; + friend const_iterator; + class iterator { + private: + RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr + map *domain; + + public: + iterator() : raw_pointer(nullptr), domain(nullptr) {} + iterator(const iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + iterator(RedBlackTreeNodeType *raw_pointer, map *domain) : raw_pointer(raw_pointer), domain(domain) {} + iterator &operator++() { + if (raw_pointer == nullptr) throw invalid_iterator(); + if (raw_pointer->right != nullptr) { + raw_pointer = raw_pointer->right; + while (raw_pointer->left != nullptr) raw_pointer = raw_pointer->left; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->right == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + iterator operator++(int) { + iterator tmp = *this; + ++*this; + return tmp; + } + iterator &operator--() { + if (raw_pointer == nullptr) { + if (domain == nullptr) throw invalid_iterator(); + raw_pointer = domain->tree_root; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + return *this; + } + if (raw_pointer->left != nullptr) { + raw_pointer = raw_pointer->left; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->left == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + throw invalid_iterator(); + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + iterator operator--(int) { + iterator tmp = *this; + --*this; + return tmp; + } + /** + * a operator to check whether two iterators are same (pointing to the same memory). + */ + value_type &operator*() const { + if (raw_pointer == nullptr) throw invalid_iterator(); + return raw_pointer->val; + } + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + + /** + * for the support of it->first. + * See + * + * for help. + */ + value_type *operator->() const noexcept { return &raw_pointer->val; } + }; + class const_iterator { + private: + RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr + const map *domain; + + public: + const_iterator() : raw_pointer(nullptr), domain(nullptr) {} + const_iterator(const const_iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + const_iterator(const iterator &other) : raw_pointer(other.raw_pointer), domain(other.domain) {} + const_iterator(RedBlackTreeNodeType *raw_pointer, const map *domain) : raw_pointer(raw_pointer), domain(domain) {} + const_iterator &operator++() { + if (raw_pointer == nullptr) throw invalid_iterator(); + if (raw_pointer->right != nullptr) { + raw_pointer = raw_pointer->right; + while (raw_pointer->left != nullptr) raw_pointer = raw_pointer->left; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->right == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + const_iterator operator++(int) { + const_iterator tmp = *this; + ++*this; + return tmp; + } + const_iterator &operator--() { + if (raw_pointer == nullptr) { + if (domain == nullptr) throw invalid_iterator(); + raw_pointer = domain->tree_root; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + return *this; + } + if (raw_pointer->left != nullptr) { + raw_pointer = raw_pointer->left; + while (raw_pointer->right != nullptr) raw_pointer = raw_pointer->right; + } else { + RedBlackTreeNodeType *backup = raw_pointer; + while (raw_pointer->parent != nullptr && raw_pointer->parent->left == raw_pointer) + raw_pointer = raw_pointer->parent; + if (raw_pointer->parent == nullptr) { + throw invalid_iterator(); + raw_pointer = nullptr; + return *this; + } + raw_pointer = raw_pointer->parent; + } + return *this; + } + const_iterator operator--(int) { + const_iterator tmp = *this; + --*this; + return tmp; + } + /** + * a operator to check whether two iterators are same (pointing to the same memory). + */ + const value_type &operator*() const { + if (raw_pointer == nullptr) throw invalid_iterator(); + return raw_pointer->val; + } + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + value_type *operator->() const noexcept { return &raw_pointer->val; } + }; + map() : node_count(0), tree_root(nullptr) {} + // TODO copy constructor + map(const map &other) {} + /** + * TODO assignment operator + */ + map &operator=(const map &other) {} + ~map() { + if (tree_root) tree_root->ReleaseAll(); + delete tree_root; + } + /** + * TODO + * access specified element with bounds checking + * Returns a reference to the mapped value of the element with key equivalent to key. + * If no such element exists, an exception of type `index_out_of_bound' + */ + T &at(const Key &key) {} + const T &at(const Key &key) const {} + /** + * TODO + * access specified element + * Returns a reference to the value that is mapped to a key equivalent to key, + * performing an insertion if such key does not already exist. + */ + T &operator[](const Key &key) {} + /** + * behave like at() throw index_out_of_bound if such key does not exist. + */ + const T &operator[](const Key &key) const {} + /** + * return a iterator to the beginning + */ + iterator begin() {} + const_iterator cbegin() const {} + /** + * return a iterator to the end + * in fact, it returns past-the-end. + */ + iterator end() {} + const_iterator cend() const {} + /** + * checks whether the container is empty + * return true if empty, otherwise false. + */ + bool empty() const { return node_count == 0; } + /** + * returns the number of elements. + */ + size_t size() const { return node_count; } + /** + * clears the contents + */ + void clear() {} + /** + * insert an element. + * return a pair, the first of the pair is + * the iterator to the new element (or the element that prevented the insertion), + * the second one is true if insert successfully, or false. + */ + pair insert(const value_type &value) { + if (tree_root == nullptr) { + tree_root = new RedBlackTreeNodeType(value, nullptr, nullptr, nullptr, RedBlackTreeNodeType::BLACK); + node_count = 1; + return pair(iterator(tree_root), true); + } + } + /** + * erase the element at pos. + * + * throw if pos pointed to a bad element (pos == this->end() || pos points an element out of this) + */ + void erase(iterator pos) {} + /** + * Returns the number of elements with key + * that compares equivalent to the specified argument, + * which is either 1 or 0 + * since this container does not allow duplicates. + * The default method of check the equivalence is !(a < b || b > a) + */ + size_t count(const Key &key) const {} + /** + * Finds an element with key equivalent to key. + * key value of the element to search for. + * Iterator to an element with key equivalent to key. + * If no such element is found, past-the-end (see end()) iterator is returned. + */ + iterator find(const Key &key) {} + const_iterator find(const Key &key) const {} }; -} +} // namespace sjtu -#endif +#endif \ No newline at end of file