write basic structure and insert
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@
|
|||||||
/build
|
/build
|
||||||
/.cache
|
/.cache
|
||||||
.clang-format
|
.clang-format
|
||||||
|
/map/src/std.hpp
|
402
map/src/map.hpp
402
map/src/map.hpp
@ -5,18 +5,16 @@
|
|||||||
#define SJTU_MAP_HPP
|
#define SJTU_MAP_HPP
|
||||||
|
|
||||||
// only for std::less<T>
|
// only for std::less<T>
|
||||||
#include <functional>
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include "utility.hpp"
|
#include <functional>
|
||||||
#include "exceptions.hpp"
|
#include "exceptions.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
namespace sjtu {
|
namespace sjtu {
|
||||||
|
|
||||||
template<
|
template <class Key, class T, class Compare = std::less<Key> >
|
||||||
class Key,
|
class map {
|
||||||
class T,
|
|
||||||
class Compare = std::less<Key>
|
|
||||||
> class map {
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* the internal type of data.
|
* the internal type of data.
|
||||||
@ -24,6 +22,189 @@ public:
|
|||||||
* You can use sjtu::map as value_type by typedef.
|
* You can use sjtu::map as value_type by typedef.
|
||||||
*/
|
*/
|
||||||
typedef pair<const Key, T> value_type;
|
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();
|
||||||
|
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.
|
* see BidirectionalIterator at CppReference for help.
|
||||||
*
|
*
|
||||||
@ -32,85 +213,178 @@ public:
|
|||||||
* or it = map.end(); ++end();
|
* or it = map.end(); ++end();
|
||||||
*/
|
*/
|
||||||
class const_iterator;
|
class const_iterator;
|
||||||
|
class iterator;
|
||||||
|
friend iterator;
|
||||||
|
friend const_iterator;
|
||||||
class iterator {
|
class iterator {
|
||||||
private:
|
private:
|
||||||
/**
|
RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr
|
||||||
* TODO add data members
|
map *domain;
|
||||||
* just add whatever you want.
|
|
||||||
*/
|
|
||||||
public:
|
public:
|
||||||
iterator() {
|
iterator() : raw_pointer(nullptr), domain(nullptr) {}
|
||||||
// TODO
|
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;
|
||||||
}
|
}
|
||||||
iterator(const iterator &other) {
|
raw_pointer = raw_pointer->parent;
|
||||||
// TODO
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 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).
|
* a operator to check whether two iterators are same (pointing to the same memory).
|
||||||
*/
|
*/
|
||||||
value_type & operator*() const {}
|
value_type &operator*() const {
|
||||||
bool operator==(const iterator &rhs) const {}
|
if (raw_pointer == nullptr) throw invalid_iterator();
|
||||||
bool operator==(const const_iterator &rhs) const {}
|
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.
|
* some other operator for iterator.
|
||||||
*/
|
*/
|
||||||
bool operator!=(const iterator &rhs) const {}
|
bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; }
|
||||||
bool operator!=(const const_iterator &rhs) const {}
|
bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* for the support of it->first.
|
* 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.
|
* 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 {}
|
value_type *operator->() const noexcept { return &raw_pointer->val; }
|
||||||
};
|
};
|
||||||
class const_iterator {
|
class const_iterator {
|
||||||
// it should has similar member method as iterator.
|
|
||||||
// and it should be able to construct from an iterator.
|
|
||||||
private:
|
private:
|
||||||
// data members.
|
RedBlackTreeNodeType *raw_pointer; // when iterator points to end(), raw_pointer=nullptr
|
||||||
|
const map *domain;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const_iterator() {
|
const_iterator() : raw_pointer(nullptr), domain(nullptr) {}
|
||||||
// TODO
|
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;
|
||||||
}
|
}
|
||||||
const_iterator(const const_iterator &other) {
|
raw_pointer = raw_pointer->parent;
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
const_iterator(const iterator &other) {
|
return *this;
|
||||||
// TODO
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
// And other methods in iterator.
|
|
||||||
// And other methods in iterator.
|
|
||||||
// And other methods in iterator.
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* TODO two constructors
|
* a operator to check whether two iterators are same (pointing to the same memory).
|
||||||
*/
|
*/
|
||||||
map() {}
|
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) {}
|
map(const map &other) {}
|
||||||
/**
|
/**
|
||||||
* TODO assignment operator
|
* TODO assignment operator
|
||||||
*/
|
*/
|
||||||
map &operator=(const map &other) {}
|
map &operator=(const map &other) {}
|
||||||
/**
|
~map() {
|
||||||
* TODO Destructors
|
if (tree_root) tree_root->ReleaseAll();
|
||||||
*/
|
delete tree_root;
|
||||||
~map() {}
|
}
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
* access specified element with bounds checking
|
* access specified element with bounds checking
|
||||||
@ -145,11 +419,11 @@ public:
|
|||||||
* checks whether the container is empty
|
* checks whether the container is empty
|
||||||
* return true if empty, otherwise false.
|
* return true if empty, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool empty() const {}
|
bool empty() const { return node_count == 0; }
|
||||||
/**
|
/**
|
||||||
* returns the number of elements.
|
* returns the number of elements.
|
||||||
*/
|
*/
|
||||||
size_t size() const {}
|
size_t size() const { return node_count; }
|
||||||
/**
|
/**
|
||||||
* clears the contents
|
* clears the contents
|
||||||
*/
|
*/
|
||||||
@ -160,7 +434,13 @@ public:
|
|||||||
* the iterator to the new element (or the element that prevented the insertion),
|
* the iterator to the new element (or the element that prevented the insertion),
|
||||||
* the second one is true if insert successfully, or false.
|
* the second one is true if insert successfully, or false.
|
||||||
*/
|
*/
|
||||||
pair<iterator, bool> insert(const value_type &value) {}
|
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), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* erase the element at pos.
|
* erase the element at pos.
|
||||||
*
|
*
|
||||||
@ -185,6 +465,6 @@ public:
|
|||||||
const_iterator find(const Key &key) const {}
|
const_iterator find(const Key &key) const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace sjtu
|
||||||
|
|
||||||
#endif
|
#endif
|
Reference in New Issue
Block a user