Files
STLite-ACM-2024/map/src/map.hpp

800 lines
29 KiB
C++

/**
* implement a container like std::map
*/
#ifndef SJTU_MAP_HPP
#define SJTU_MAP_HPP
// only for std::less<T>
#include <cassert>
#include <cstddef>
#include <functional>
#ifndef NDEBUG
#include <queue> // only for debug use
#include <vector> // only for debug use
#endif
#include "exceptions.hpp"
#include "utility.hpp"
namespace sjtu {
template <class Key, class T, class Compare = std::less<Key> >
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<const Key, T> value_type;
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();
RedBlackTreeNodeType *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) {
RedBlackTreeNodeType *old_parent = parent;
parent->RotateLeft(tree_root);
assert(old_parent->parent == this);
old_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) {
RedBlackTreeNodeType *old_parent = parent;
parent->RotateRight(tree_root);
assert(old_parent->parent == this);
old_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 and where the insertion is.
*
* @note Note that tree_root is a reference to the root of the tree. This function will modify the tree_root if
* necessary.
*/
std::pair<RedBlackTreeNodeType *, 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;
RedBlackTreeNodeType *addr = left;
left->InsertFixUp(tree_root);
return std::pair<RedBlackTreeNodeType *, bool>(addr, true);
} 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;
RedBlackTreeNodeType *addr = right;
right->InsertFixUp(tree_root);
return std::pair<RedBlackTreeNodeType *, bool>(addr, true);
} else {
return right->Insert(tree_root, val, allow_replacement);
}
} else {
if (allow_replacement) {
this->val.second = val.second;
return std::pair<RedBlackTreeNodeType *, bool>(this, false);
}
}
return std::pair<RedBlackTreeNodeType *, bool>(this, 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;
}
RedBlackTreeNodeType *Find(const decltype(val.first) &key) {
if (comparer(key, val.first)) {
if (left == nullptr) return nullptr;
return left->Find(key);
} else if (comparer(val.first, key)) {
if (right == nullptr) return nullptr;
return right->Find(key);
} else {
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 = 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;
}
}
void DeleteFixUp(RedBlackTreeNodeType *&tree_root) {
assert(this->color == RedBlackTreeColorType::BLACK);
if (this == tree_root) return;
RedBlackTreeNodeType *sibling = GetSibling();
assert(sibling != nullptr);
if (sibling->color == RedBlackTreeColorType::RED) {
// Case 1
parent->color = RedBlackTreeColorType::RED;
sibling->color = RedBlackTreeColorType::BLACK;
if (this == parent->left) {
parent->RotateLeft(tree_root);
} else {
parent->RotateRight(tree_root);
}
this->DeleteFixUp(tree_root);
return;
}
RedBlackTreeNodeType *close_nephew = nullptr;
RedBlackTreeNodeType *distant_nephew = nullptr;
if (this == parent->left) {
close_nephew = sibling->left;
distant_nephew = sibling->right;
} else {
close_nephew = sibling->right;
distant_nephew = sibling->left;
}
if (sibling->color == RedBlackTreeColorType::BLACK && this->parent->color == RedBlackTreeColorType::RED &&
(close_nephew == nullptr || close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::BLACK) &&
(distant_nephew == nullptr ||
distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::BLACK)) {
// Case 2
sibling->color = RedBlackTreeColorType::RED;
this->parent->color = RedBlackTreeColorType::BLACK;
return;
}
if (sibling->color == RedBlackTreeColorType::BLACK && this->parent->color == RedBlackTreeColorType::BLACK &&
(close_nephew == nullptr || close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::BLACK) &&
(distant_nephew == nullptr ||
distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::BLACK)) {
// Case 3
sibling->color = RedBlackTreeColorType::RED;
this->parent->DeleteFixUp(tree_root);
return;
}
if (close_nephew != nullptr && close_nephew->color == RedBlackTreeColorType::RED) {
// Case 4
if (this == parent->left) {
sibling->color = RedBlackTreeColorType::RED;
close_nephew->color = RedBlackTreeColorType::BLACK;
sibling->RotateRight(tree_root);
} else {
sibling->color = RedBlackTreeColorType::RED;
close_nephew->color = RedBlackTreeColorType::BLACK;
sibling->RotateLeft(tree_root);
}
this->DeleteFixUp(tree_root);
return;
}
assert(distant_nephew != nullptr && distant_nephew->color == RedBlackTreeColorType::RED);
// Then it must be Case 5
if (this == parent->left) {
std::swap(sibling->color, parent->color);
distant_nephew->color = RedBlackTreeColorType::BLACK;
parent->RotateLeft(tree_root);
} else {
std::swap(sibling->color, parent->color);
distant_nephew->color = RedBlackTreeColorType::BLACK;
parent->RotateRight(tree_root);
}
}
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(pos, tree_root);
return;
}
if (pos->left == nullptr && pos->right == nullptr) {
// Case 2
if (pos->color == RedBlackTreeColorType::BLACK) {
pos->DeleteFixUp(tree_root);
}
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;
}
static void CopyFrom(RedBlackTreeNodeType *&target, const RedBlackTreeNodeType *source) {
if (source == nullptr) return;
target = new RedBlackTreeNodeType(source->val, nullptr, nullptr, nullptr, source->color);
CopyFrom(target->left, source->left);
CopyFrom(target->right, source->right);
target->SetChildrensParent();
}
};
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:
// Add some type traits
typedef std::bidirectional_iterator_tag iterator_category;
typedef pair<const Key, T> value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type *pointer;
typedef value_type &reference;
friend const_iterator;
friend map;
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();
if (domain->tree_root == 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 domain == rhs.domain && raw_pointer == rhs.raw_pointer; }
bool operator==(const const_iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; }
/**
* some other operator for iterator.
*/
bool operator!=(const iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; }
bool operator!=(const const_iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; }
/**
* for the support of it->first.
* See
* <http://kelvinh.github.io/blog/2013/11/20/overloading-of-member-access-operator-dash-greater-than-symbol-in-cpp/>
* 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:
// Add some type traits
typedef std::bidirectional_iterator_tag iterator_category;
typedef pair<const Key, T> value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type *pointer;
typedef value_type &reference;
friend iterator;
friend map;
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();
if (domain->tree_root == 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 domain == rhs.domain && raw_pointer == rhs.raw_pointer; }
bool operator==(const const_iterator &rhs) const { return domain == rhs.domain && raw_pointer == rhs.raw_pointer; }
/**
* some other operator for iterator.
*/
bool operator!=(const iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; }
bool operator!=(const const_iterator &rhs) const { return domain != rhs.domain || raw_pointer != rhs.raw_pointer; }
value_type *operator->() const noexcept { return &raw_pointer->val; }
};
map() : node_count(0), tree_root(nullptr) {}
map(const map &other) {
node_count = other.node_count;
tree_root = nullptr;
RedBlackTreeNodeType::CopyFrom(tree_root, other.tree_root);
}
map(map &&other) {
node_count = other.node_count;
tree_root = other.tree_root;
other.node_count = 0;
other.tree_root = nullptr;
}
/**
* assignment operator
*/
map &operator=(const map &other) {
if (this == &other) return *this;
if (tree_root) tree_root->ReleaseAll();
delete tree_root;
node_count = other.node_count;
RedBlackTreeNodeType::CopyFrom(tree_root, other.tree_root);
return *this;
}
map &operator=(map &&other) {
if (this == &other) return *this;
if (tree_root) tree_root->ReleaseAll();
delete tree_root;
node_count = other.node_count;
tree_root = other.tree_root;
other.node_count = 0;
other.tree_root = nullptr;
return *this;
}
~map() {
if (tree_root) tree_root->ReleaseAll();
delete tree_root;
}
/**
* 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) {
if (tree_root == nullptr) throw index_out_of_bound();
RedBlackTreeNodeType *result = tree_root->Find(key);
if (result == nullptr) throw index_out_of_bound();
return result->val.second;
}
const T &at(const Key &key) const {
if (tree_root == nullptr) throw index_out_of_bound();
RedBlackTreeNodeType *result = tree_root->Find(key);
if (result == nullptr) throw index_out_of_bound();
return result->val.second;
}
/**
* 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) {
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;
}
/**
* behave like at() throw index_out_of_bound if such key does not exist.
*/
const T &operator[](const Key &key) const { return at(key); }
/**
* return a iterator to the beginning
*/
iterator begin() {
if (tree_root == nullptr) return iterator(nullptr, this);
RedBlackTreeNodeType *tmp = tree_root;
while (tmp->left != nullptr) tmp = tmp->left;
return iterator(tmp, this);
}
const_iterator cbegin() const {
if (tree_root == nullptr) return const_iterator(nullptr, this);
RedBlackTreeNodeType *tmp = tree_root;
while (tmp->left != nullptr) tmp = tmp->left;
return const_iterator(tmp, this);
}
/**
* return a iterator to the end
* in fact, it returns past-the-end.
*/
iterator end() { return iterator(nullptr, this); }
const_iterator cend() const { return const_iterator(nullptr, this); }
/**
* 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() {
if (tree_root) tree_root->ReleaseAll();
delete tree_root;
tree_root = nullptr;
node_count = 0;
}
/**
* 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<iterator, bool> 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, bool>(iterator(tree_root, this), true);
}
auto result = tree_root->Insert(tree_root, value, false);
if (result.second) ++node_count;
return pair<iterator, bool>(iterator(result.first, this), result.second);
}
/**
* 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) {
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
* 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 {
if (tree_root == nullptr) return 0;
return tree_root->Find(key) == nullptr ? 0 : 1;
}
/**
* 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) {
if (tree_root == nullptr) return end();
return iterator(tree_root->Find(key), this);
}
const_iterator find(const Key &key) const {
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>
Compare map<Key, T, Compare>::comparer = Compare();
} // namespace sjtu
#endif