ready to finish erase

This commit is contained in:
2024-03-26 08:20:30 +00:00
parent 68d185f12a
commit e27b18df67
3 changed files with 207 additions and 6 deletions

View File

@ -1,3 +1,9 @@
参考资料:
- <https://zh.wikipedia.org/zh-hans/%E7%BA%A2%E9%BB%91%E6%A0%91>[镜像](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://oi-wiki.org/ds/rbtree/>[备份](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`文件内所要求的内容即可。 - 这次大作业要求实现与 C++ 标准库类似的数据结构 map包括迭代器等。框架接口在已本仓库中给出只需要实现`.hpp`文件内所要求的内容即可。

View File

@ -8,6 +8,8 @@
#include <cassert> #include <cassert>
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
#include <queue> // only for debug use
#include <vector> // only for debug use
#include "exceptions.hpp" #include "exceptions.hpp"
#include "utility.hpp" #include "utility.hpp"
@ -126,9 +128,10 @@ class map {
} }
if (grand_parent->left == parent) { if (grand_parent->left == parent) {
if (parent->right == this) { if (parent->right == this) {
RedBlackTreeNodeType *old_parent = parent;
parent->RotateLeft(tree_root); parent->RotateLeft(tree_root);
assert(parent->parent == this); assert(old_parent->parent == this);
parent->InsertFixUp(tree_root); old_parent->InsertFixUp(tree_root);
return; return;
} }
grand_parent->RotateRight(tree_root); grand_parent->RotateRight(tree_root);
@ -137,9 +140,10 @@ class map {
grand_parent->color = RedBlackTreeColorType::RED; grand_parent->color = RedBlackTreeColorType::RED;
} else { } else {
if (parent->left == this) { if (parent->left == this) {
RedBlackTreeNodeType *old_parent = parent;
parent->RotateRight(tree_root); parent->RotateRight(tree_root);
assert(parent->parent == this); assert(old_parent->parent == this);
parent->InsertFixUp(tree_root); old_parent->InsertFixUp(tree_root);
return; return;
} }
grand_parent->RotateLeft(tree_root); grand_parent->RotateLeft(tree_root);
@ -168,8 +172,9 @@ class map {
if (left == nullptr) { if (left == nullptr) {
left = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); left = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED);
left->parent = this; left->parent = this;
RedBlackTreeNodeType *addr = left;
left->InsertFixUp(tree_root); left->InsertFixUp(tree_root);
return std::pair<RedBlackTreeNodeType *, bool>(left, true); return std::pair<RedBlackTreeNodeType *, bool>(addr, true);
} else { } else {
return left->Insert(tree_root, val, allow_replacement); return left->Insert(tree_root, val, allow_replacement);
} }
@ -177,8 +182,9 @@ class map {
if (right == nullptr) { if (right == nullptr) {
right = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED); right = new RedBlackTreeNodeType(val, nullptr, nullptr, this, RedBlackTreeColorType::RED);
right->parent = this; right->parent = this;
RedBlackTreeNodeType *addr = right;
right->InsertFixUp(tree_root); right->InsertFixUp(tree_root);
return std::pair<RedBlackTreeNodeType *, bool>(right, true); return std::pair<RedBlackTreeNodeType *, bool>(addr, true);
} else { } else {
return right->Insert(tree_root, val, allow_replacement); return right->Insert(tree_root, val, allow_replacement);
} }
@ -214,6 +220,81 @@ class map {
return this; 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; size_t node_count;
RedBlackTreeNodeType *tree_root; RedBlackTreeNodeType *tree_root;
@ -470,6 +551,12 @@ class map {
* performing an insertion if such key does not already exist. * performing an insertion if such key does not already exist.
*/ */
T &operator[](const Key &key) { 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); auto result = tree_root->Insert(tree_root, value_type(key, T()), false);
if (result.second) ++node_count; if (result.second) ++node_count;
return result.first->val.second; return result.first->val.second;
@ -540,6 +627,8 @@ class map {
*/ */
void erase(iterator pos) { void erase(iterator pos) {
if (pos.domain != this || pos.raw_pointer == nullptr) throw invalid_iterator(); 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 * Returns the number of elements with key
@ -566,6 +655,63 @@ class map {
if (tree_root == nullptr) return cend(); if (tree_root == nullptr) return cend();
return const_iterator(tree_root->Find(key), this); 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<RedBlackTreeNodeType *> Q;
std::vector<RedBlackTreeNodeType *> 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> key_array;
std::function<void(RedBlackTreeNodeType *)> 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. // Define the static member comparer.
template <class Key, class T, class Compare> template <class Key, class T, class Compare>

View File

@ -1,4 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iostream>
#include <random>
#include "map.hpp" #include "map.hpp"
TEST(BasicTests, GTestItSelf) { TEST(BasicTests, GTestItSelf) {
@ -12,4 +14,51 @@ TEST(BasicTests, ConstructorAndEmptySize) {
sjtu::map<int, int> map; sjtu::map<int, int> map;
EXPECT_EQ(map.empty(), true); EXPECT_EQ(map.empty(), true);
EXPECT_EQ(map.size(), 0); EXPECT_EQ(map.size(), 0);
}
TEST(BasicTests, BasicOperatorInsert) {
sjtu::map<int, int> 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<int, int> map2;
auto ret = map2.insert(sjtu::pair<int, int>(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<int, int> map;
std::map<int, int> 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);
}
} }