ready to finish erase
This commit is contained in:
@ -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`文件内所要求的内容即可。
|
||||
|
158
map/src/map.hpp
158
map/src/map.hpp
@ -8,6 +8,8 @@
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <queue> // only for debug use
|
||||
#include <vector> // 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<RedBlackTreeNodeType *, bool>(left, true);
|
||||
return std::pair<RedBlackTreeNodeType *, bool>(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<RedBlackTreeNodeType *, bool>(right, true);
|
||||
return std::pair<RedBlackTreeNodeType *, bool>(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<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.
|
||||
template <class Key, class T, class Compare>
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include "map.hpp"
|
||||
|
||||
TEST(BasicTests, GTestItSelf) {
|
||||
@ -13,3 +15,50 @@ TEST(BasicTests, ConstructorAndEmptySize) {
|
||||
EXPECT_EQ(map.empty(), true);
|
||||
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user