From e27b18df677e8e0550597ac45af60343822eba8e Mon Sep 17 00:00:00 2001 From: ZhuangYumin Date: Tue, 26 Mar 2024 08:20:30 +0000 Subject: [PATCH] ready to finish erase --- map/README.md | 6 ++ map/src/map.hpp | 158 ++++++++++++++++++++++++++++++++++++++-- map/test/test_basic.cpp | 49 +++++++++++++ 3 files changed, 207 insertions(+), 6 deletions(-) diff --git a/map/README.md b/map/README.md index 0397132..8ae817b 100644 --- a/map/README.md +++ b/map/README.md @@ -1,3 +1,9 @@ +参考资料: +- ([镜像](https://cloud.zymsite.ink/f/Wx4hJ/%E7%BA%A2%E9%BB%91%E6%A0%91%20-%20%E7%BB%B4%E5%9F%BA%E7%99%BE%E7%A7%91%EF%BC%8C%E8%87%AA%E7%94%B1%E7%9A%84%E7%99%BE%E7%A7%91%E5%85%A8%E4%B9%A6%20%282024_3_26%2016_16_20%29.html)) +- ([备份](https://cloud.zymsite.ink/f/EWeto/%E7%BA%A2%E9%BB%91%E6%A0%91%20-%20OI%20Wiki%20%282024_3_26%2016_14_29%29.html)) + +___ + ## 作业要求 - 这次大作业要求实现与 C++ 标准库类似的数据结构 map,包括迭代器等。框架接口在已本仓库中给出,只需要实现`.hpp`文件内所要求的内容即可。 diff --git a/map/src/map.hpp b/map/src/map.hpp index 44cebf2..1aaa76a 100644 --- a/map/src/map.hpp +++ b/map/src/map.hpp @@ -8,6 +8,8 @@ #include #include #include +#include // only for debug use +#include // only for debug use #include "exceptions.hpp" #include "utility.hpp" @@ -126,9 +128,10 @@ class map { } if (grand_parent->left == parent) { if (parent->right == this) { + RedBlackTreeNodeType *old_parent = parent; parent->RotateLeft(tree_root); - assert(parent->parent == this); - parent->InsertFixUp(tree_root); + assert(old_parent->parent == this); + old_parent->InsertFixUp(tree_root); return; } grand_parent->RotateRight(tree_root); @@ -137,9 +140,10 @@ class map { grand_parent->color = RedBlackTreeColorType::RED; } else { if (parent->left == this) { + RedBlackTreeNodeType *old_parent = parent; parent->RotateRight(tree_root); - assert(parent->parent == this); - parent->InsertFixUp(tree_root); + assert(old_parent->parent == this); + old_parent->InsertFixUp(tree_root); return; } grand_parent->RotateLeft(tree_root); @@ -168,8 +172,9 @@ class map { if (left == nullptr) { left = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); left->parent = this; + RedBlackTreeNodeType *addr = left; left->InsertFixUp(tree_root); - return std::pair(left, true); + return std::pair(addr, true); } else { return left->Insert(tree_root, val, allow_replacement); } @@ -177,8 +182,9 @@ class map { if (right == nullptr) { right = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); right->parent = this; + RedBlackTreeNodeType *addr = right; right->InsertFixUp(tree_root); - return std::pair(right, true); + return std::pair(addr, true); } else { return right->Insert(tree_root, val, allow_replacement); } @@ -214,6 +220,81 @@ class map { return this; } } + /** + * @brief Swap the node with its successor. + * + * @details This function will swap the node with its successor. + * + * @note The color is not swapped. + */ + inline static void SwapNodeWithItsSuccessor(RedBlackTreeNodeType *node, RedBlackTreeNodeType *successor, + RedBlackTreeNodeType *&tree_root) { + RedBlackTreeNodeType *left_of_node = node->left; + RedBlackTreeNodeType *right_of_node = node->right; + RedBlackTreeNodeType *parent_of_node = node->parent; + RedBlackTreeColorType color_of_node = node->color; + RedBlackTreeNodeType *&path_of_node = node->GetSelfPath(tree_root); + RedBlackTreeNodeType *left_of_successor = successor->left; + RedBlackTreeNodeType *right_of_successor = successor->right; + RedBlackTreeNodeType *parent_of_successor = successor->parent; + RedBlackTreeColorType color_of_successor = successor->color; + RedBlackTreeNodeType *&path_of_successor = successor->GetSelfPath(tree_root); + node->color = color_of_successor; + successor->color = color_of_node; + if (parent_of_successor == node) { + successor->left = left_of_node; + successor->right = right_of_node; + successor->SetChildrensParent(); + successor->parent = parent_of_node; + path_of_node = successor; + node->left = left_of_successor; + node->right = right_of_successor; + node->SetChildrensParent(); + } else { + successor->left = left_of_node; + successor->right = right_of_node; + successor->SetChildrensParent(); + successor->parent = parent_of_node; + path_of_node = successor; + node->left = left_of_successor; + node->right = right_of_successor; + node->SetChildrensParent(); + node->parent = parent_of_successor; + path_of_successor = node; + } + } + static void DeleteNode(RedBlackTreeNodeType *pos, RedBlackTreeNodeType *&tree_root) { + if (pos->parent == nullptr && pos->left == nullptr && pos->right == nullptr) { + // Case 0: The only node in the tree. + delete pos; + tree_root = nullptr; + return; + } + if (pos->left != nullptr && pos->right != nullptr) { + // Case 1: The node has two children. Then we swap the node with its successor and just delete the successor. + RedBlackTreeNodeType *successor = pos->right; + while (successor->left != nullptr) successor = successor->left; + SwapNodeWithItsSuccessor(pos, successor, tree_root); + DeleteNode(successor, tree_root); + return; + } + if (pos->left == nullptr && pos->right == nullptr) { + // Case 2 + if (pos->color == RedBlackTreeColorType::RED) { + pos->GetSelfPath(tree_root) = nullptr; + delete pos; + return; + } + } + // Case 3 + RedBlackTreeNodeType *replacement = (pos->left != nullptr ? pos->left : pos->right); + assert(replacement != nullptr); + assert(replacement->color == RedBlackTreeColorType::RED); + pos->GetSelfPath(tree_root) = replacement; + replacement->parent = pos->parent; + replacement->color = RedBlackTreeColorType::BLACK; + delete pos; + } }; size_t node_count; RedBlackTreeNodeType *tree_root; @@ -470,6 +551,12 @@ class map { * performing an insertion if such key does not already exist. */ T &operator[](const Key &key) { + if (node_count == 0) { + tree_root = + new RedBlackTreeNodeType(value_type(key, T()), nullptr, nullptr, nullptr, RedBlackTreeNodeType::BLACK); + ++node_count; + return tree_root->val.second; + } auto result = tree_root->Insert(tree_root, value_type(key, T()), false); if (result.second) ++node_count; return result.first->val.second; @@ -540,6 +627,8 @@ class map { */ void erase(iterator pos) { if (pos.domain != this || pos.raw_pointer == nullptr) throw invalid_iterator(); + RedBlackTreeNodeType::DeleteNode(pos.raw_pointer, tree_root); + --node_count; } /** * Returns the number of elements with key @@ -566,6 +655,63 @@ class map { if (tree_root == nullptr) return cend(); return const_iterator(tree_root->Find(key), this); } +#ifndef NDEBUG + bool RedBlackTreeStructureCheck() { + if (tree_root == nullptr) return node_count == 0; + if (node_count == 0) return false; + std::queue Q; + std::vector NIL_leafs; + size_t actual_node_count = 0; + Q.push(tree_root); + while (!Q.empty()) { + RedBlackTreeNodeType *current = Q.front(); + Q.pop(); + if (current->color == RedBlackTreeNodeType::RED) { + if (current->left != nullptr && current->left->color == RedBlackTreeNodeType::RED) return false; + if (current->right != nullptr && current->right->color == RedBlackTreeNodeType::RED) return false; + } + if (current->left != nullptr) + Q.push(current->left); + else + NIL_leafs.push_back(current); + if (current->right != nullptr) + Q.push(current->right); + else + NIL_leafs.push_back(current); + ++actual_node_count; + } + if (actual_node_count != node_count) return false; + if (tree_root->color != RedBlackTreeNodeType::BLACK) return false; + if (tree_root->parent != nullptr) return false; + assert(NIL_leafs.size() >= 2); + size_t correct_black_nodes = 1; + RedBlackTreeNodeType *ptr = NIL_leafs[0]; + while (ptr) { + if (ptr->color == RedBlackTreeNodeType::BLACK) ++correct_black_nodes; + ptr = ptr->parent; + } + for (auto ptr : NIL_leafs) { + size_t black_nodes = 1; + while (ptr) { + if (ptr->color == RedBlackTreeNodeType::BLACK) ++black_nodes; + ptr = ptr->parent; + } + if (black_nodes != correct_black_nodes) return false; + } + // Now check whether it is a binary search tree. Use a lambda expression to get inorder tree walk. + std::vector key_array; + std::function inorder_walk = [&](RedBlackTreeNodeType *node) { + if (node->left != nullptr) inorder_walk(node->left); + key_array.push_back(node->val.first); + if (node->right != nullptr) inorder_walk(node->right); + }; + inorder_walk(tree_root); + for (size_t i = 1; i < key_array.size(); ++i) { + if (!comparer(key_array[i - 1], key_array[i])) return false; + } + return true; + } +#endif }; // Define the static member comparer. template diff --git a/map/test/test_basic.cpp b/map/test/test_basic.cpp index d07353b..3f7f461 100644 --- a/map/test/test_basic.cpp +++ b/map/test/test_basic.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "map.hpp" TEST(BasicTests, GTestItSelf) { @@ -12,4 +14,51 @@ TEST(BasicTests, ConstructorAndEmptySize) { sjtu::map map; EXPECT_EQ(map.empty(), true); EXPECT_EQ(map.size(), 0); +} + +TEST(BasicTests, BasicOperatorInsert) { + sjtu::map map1; + map1[1] = 2; + EXPECT_EQ(map1.empty(), false); + EXPECT_EQ(map1.size(), 1); + EXPECT_EQ(map1[1], 2); + auto it = map1.find(1); + EXPECT_EQ(it->second, 2); + sjtu::map map2; + auto ret = map2.insert(sjtu::pair(1, 2)); + EXPECT_EQ(ret.second, true); + EXPECT_EQ(ret.first->first, 1); + EXPECT_EQ(ret.first->second, 2); +} + +TEST(BasicTests, MassiveOperatorInsert) { + std::mt19937 rnd(2333); + sjtu::map map; + std::map standard_map; + int nodes = 1000; + for (int i = 0; i < nodes; ++i) { + int key = rnd() % 10000; + int value = rnd() % 10000; + // std::cerr << "writing " << key << " " << value << "\n"; + map[key] = value; +#ifndef NDEBUG + EXPECT_TRUE(map.RedBlackTreeStructureCheck()); +#endif + standard_map[key] = value; + } + auto mp_it = map.begin(); + auto st_it = standard_map.begin(); + EXPECT_EQ(map.size(), standard_map.size()); + for (; mp_it != map.end(); mp_it++, st_it++) { + EXPECT_EQ(mp_it->first, st_it->first); + EXPECT_EQ(mp_it->second, st_it->second); + } + mp_it = map.end(); + st_it = standard_map.end(); + for (int i = 0; i < map.size(); ++i) { + mp_it--; + st_it--; + EXPECT_EQ(mp_it->first, st_it->first); + EXPECT_EQ(mp_it->second, st_it->second); + } } \ No newline at end of file