manually integrate stlite
This commit is contained in:
39
stlite/exceptions.hpp
Normal file
39
stlite/exceptions.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef SJTU_EXCEPTIONS_HPP
|
||||||
|
#define SJTU_EXCEPTIONS_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
|
||||||
|
class exception {
|
||||||
|
protected:
|
||||||
|
const std::string variant = "";
|
||||||
|
std::string detail = "";
|
||||||
|
public:
|
||||||
|
exception() {}
|
||||||
|
exception(const exception &ec) : variant(ec.variant), detail(ec.detail) {}
|
||||||
|
virtual std::string what() {
|
||||||
|
return variant + " " + detail;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class index_out_of_bound : public exception {
|
||||||
|
/* __________________________ */
|
||||||
|
};
|
||||||
|
|
||||||
|
class runtime_error : public exception {
|
||||||
|
/* __________________________ */
|
||||||
|
};
|
||||||
|
|
||||||
|
class invalid_iterator : public exception {
|
||||||
|
/* __________________________ */
|
||||||
|
};
|
||||||
|
|
||||||
|
class container_is_empty : public exception {
|
||||||
|
/* __________________________ */
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
429
stlite/list.hpp
Normal file
429
stlite/list.hpp
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
#ifndef SJTU_LIST_HPP
|
||||||
|
#define SJTU_LIST_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <exception>
|
||||||
|
namespace sjtu {
|
||||||
|
/**
|
||||||
|
* a data container like std::list
|
||||||
|
* allocate random memory addresses for data and they are doubly-linked in a
|
||||||
|
* list.
|
||||||
|
*/
|
||||||
|
template<typename T>class list {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
class node {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add data members and constructors & destructor
|
||||||
|
*/
|
||||||
|
T val;
|
||||||
|
node *prev, *next;
|
||||||
|
node() : prev(nullptr), next(nullptr) {}
|
||||||
|
|
||||||
|
node(const T &val, node *prev = nullptr, node *next = nullptr) :
|
||||||
|
val(val), prev(prev), next(next) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add data members for linked list as protected members
|
||||||
|
*/
|
||||||
|
node *head, *tail;
|
||||||
|
int element_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert node cur before node pos
|
||||||
|
* return the inserted node cur
|
||||||
|
*/
|
||||||
|
node* insert(node *pos, node *cur) {
|
||||||
|
if (pos->prev != nullptr) pos->prev->next = cur;
|
||||||
|
cur->prev = pos->prev;
|
||||||
|
cur->next = pos;
|
||||||
|
pos->prev = cur;
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove node pos from list (no need to delete the node)
|
||||||
|
* return the removed node pos
|
||||||
|
*/
|
||||||
|
node* erase(node *pos) {
|
||||||
|
if (pos->prev != nullptr) pos->prev->next = pos->next;
|
||||||
|
if (pos->next != nullptr) pos->next->prev = pos->prev;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
class const_iterator;
|
||||||
|
class iterator {
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO add data members
|
||||||
|
* just add whatever you want.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool is_end;
|
||||||
|
node *cur;
|
||||||
|
iterator() : is_end(true), cur(nullptr) {}
|
||||||
|
|
||||||
|
iterator(node *cur, bool is_end = false) : is_end(is_end), cur(cur) {}
|
||||||
|
|
||||||
|
iterator operator++(int) {
|
||||||
|
iterator res = *this;
|
||||||
|
++(*this);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator& operator++() {
|
||||||
|
if (cur != nullptr) {
|
||||||
|
if (cur->next != nullptr) cur = cur->next;
|
||||||
|
else is_end = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator--(int) {
|
||||||
|
iterator res = *this;
|
||||||
|
--(*this);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator& operator--() {
|
||||||
|
if (is_end) is_end = false;
|
||||||
|
else if (cur != nullptr) cur = cur->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO *it
|
||||||
|
* throw std::exception if iterator is invalid
|
||||||
|
*/
|
||||||
|
T& operator*() const {
|
||||||
|
if ((cur == nullptr) || is_end) throw std::exception();
|
||||||
|
return cur->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO it->field
|
||||||
|
* throw std::exception if iterator is invalid
|
||||||
|
*/
|
||||||
|
T * operator->() const noexcept {
|
||||||
|
if ((cur == nullptr) || is_end) throw std::exception();
|
||||||
|
return &(cur->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a operator to check whether two iterators are same (pointing to the same
|
||||||
|
* memory).
|
||||||
|
*/
|
||||||
|
bool operator==(const iterator &rhs) const {
|
||||||
|
return cur == rhs.cur && is_end == rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const const_iterator &rhs) const {
|
||||||
|
return cur == rhs.cur && is_end == rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* some other operator for iterator.
|
||||||
|
*/
|
||||||
|
bool operator!=(const iterator &rhs) const {
|
||||||
|
return cur != rhs.cur || is_end != rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const const_iterator &rhs) const {
|
||||||
|
return cur != rhs.cur || is_end != rhs.is_end;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* has same function as iterator, just for a const object.
|
||||||
|
* should be able to construct from an iterator.
|
||||||
|
*/
|
||||||
|
class const_iterator {
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool is_end;
|
||||||
|
node *cur;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO add data members
|
||||||
|
* just add whatever you want.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const_iterator() : is_end(true), cur(nullptr) {}
|
||||||
|
|
||||||
|
const_iterator(const iterator &other) : is_end(other.is_end),
|
||||||
|
cur(other.cur) {}
|
||||||
|
|
||||||
|
const_iterator(node *cur, bool is_end = false) : is_end(is_end),
|
||||||
|
cur(cur) {}
|
||||||
|
|
||||||
|
const_iterator operator++(int) {
|
||||||
|
const_iterator res = *this;
|
||||||
|
++(*this);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator& operator++() {
|
||||||
|
if (cur != nullptr) {
|
||||||
|
if (cur->next != nullptr) cur = cur->next;
|
||||||
|
else is_end = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator operator--(int) {
|
||||||
|
const_iterator res = *this;
|
||||||
|
--(*this);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator& operator--() {
|
||||||
|
if (is_end) is_end = false;
|
||||||
|
else if (cur != nullptr) cur = cur->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO *it
|
||||||
|
* throw std::exception if iterator is invalid
|
||||||
|
*/
|
||||||
|
const T& operator*() const {
|
||||||
|
if ((cur == nullptr) || is_end) throw std::exception();
|
||||||
|
return cur->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO it->field
|
||||||
|
* throw std::exception if iterator is invalid
|
||||||
|
*/
|
||||||
|
const T * operator->() const noexcept {
|
||||||
|
if ((cur == nullptr) || is_end) throw std::exception();
|
||||||
|
return &(cur->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a operator to check whether two iterators are same (pointing to the same
|
||||||
|
* memory).
|
||||||
|
*/
|
||||||
|
bool operator==(const iterator &rhs) const {
|
||||||
|
return cur == rhs.cur && is_end == rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const const_iterator &rhs) const {
|
||||||
|
return cur == rhs.cur && is_end == rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* some other operator for iterator.
|
||||||
|
*/
|
||||||
|
bool operator!=(const iterator &rhs) const {
|
||||||
|
return cur != rhs.cur || is_end != rhs.is_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const const_iterator &rhs) const {
|
||||||
|
return cur != rhs.cur || is_end != rhs.is_end;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Constructs
|
||||||
|
* Atleast two: default constructor, copy constructor
|
||||||
|
*/
|
||||||
|
list() : head(nullptr), tail(nullptr), element_count(0) {}
|
||||||
|
|
||||||
|
void copy(const list &other) {
|
||||||
|
head = nullptr;
|
||||||
|
tail = nullptr;
|
||||||
|
element_count = 0;
|
||||||
|
if (other.element_count != 0) {
|
||||||
|
head = tail = new node();
|
||||||
|
head->val = other.head->val;
|
||||||
|
node *p = other.head->next;
|
||||||
|
while (p != nullptr) {
|
||||||
|
tail->next = new node(p->val, tail);
|
||||||
|
tail = tail->next;
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
element_count = other.element_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list(const list &other) {
|
||||||
|
copy(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Destructor
|
||||||
|
*/
|
||||||
|
virtual ~list() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Assignment operator
|
||||||
|
*/
|
||||||
|
list& operator=(const list &other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
clear();
|
||||||
|
copy(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access the first / last element
|
||||||
|
* throw container_is_empty when the container is empty.
|
||||||
|
*/
|
||||||
|
const T& front() const {
|
||||||
|
if (element_count == 0) throw std::exception();
|
||||||
|
return head->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& back() const {
|
||||||
|
if (element_count == 0) throw std::exception();
|
||||||
|
return tail->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an iterator to the beginning.
|
||||||
|
*/
|
||||||
|
iterator begin() {
|
||||||
|
return iterator(head, head == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const {
|
||||||
|
return const_iterator(head, head == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an iterator to the end.
|
||||||
|
*/
|
||||||
|
iterator end() {
|
||||||
|
return iterator(tail, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const {
|
||||||
|
return const_iterator(tail, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks whether the container is empty.
|
||||||
|
*/
|
||||||
|
virtual bool empty() const {
|
||||||
|
return element_count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the number of elements
|
||||||
|
*/
|
||||||
|
virtual size_t size() const {
|
||||||
|
return element_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clears the contents
|
||||||
|
*/
|
||||||
|
virtual void clear() {
|
||||||
|
node *p = head;
|
||||||
|
while (p != nullptr) {
|
||||||
|
node *p_nxt = p->next;
|
||||||
|
delete p;
|
||||||
|
p = p_nxt;
|
||||||
|
}
|
||||||
|
head = nullptr;
|
||||||
|
tail = nullptr;
|
||||||
|
element_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert value before pos (pos may be the end() iterator)
|
||||||
|
* return an iterator pointing to the inserted value
|
||||||
|
* throw if the iterator is invalid
|
||||||
|
*/
|
||||||
|
virtual iterator insert(iterator pos, const T &value) {
|
||||||
|
if (!pos.is_end) {
|
||||||
|
node *cur = new node(value);
|
||||||
|
insert(pos.cur, cur);
|
||||||
|
if (pos.cur == head) head = cur;
|
||||||
|
element_count++;
|
||||||
|
return iterator(cur);
|
||||||
|
} else {
|
||||||
|
if (element_count == 0) {
|
||||||
|
head = tail = new node(value);
|
||||||
|
element_count++;
|
||||||
|
return iterator(tail);
|
||||||
|
}
|
||||||
|
tail->next = new node(value, tail);
|
||||||
|
tail = tail->next;
|
||||||
|
element_count++;
|
||||||
|
return iterator(tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove the element at pos (the end() iterator is invalid)
|
||||||
|
* returns an iterator pointing to the following element, if pos pointing to
|
||||||
|
* the last element, end() will be returned. throw if the container is empty,
|
||||||
|
* the iterator is invalid
|
||||||
|
*/
|
||||||
|
virtual iterator erase(iterator pos) {
|
||||||
|
if ((element_count == 0) || pos.is_end) throw std::exception();
|
||||||
|
iterator res = pos;
|
||||||
|
++res;
|
||||||
|
if (pos.cur == head) {
|
||||||
|
head = head->next;
|
||||||
|
}
|
||||||
|
if (pos.cur == tail) {
|
||||||
|
tail = tail->prev;
|
||||||
|
}
|
||||||
|
delete erase(pos.cur);
|
||||||
|
element_count--;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds an element to the end
|
||||||
|
*/
|
||||||
|
void push_back(const T &value) {
|
||||||
|
insert(end(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes the last element
|
||||||
|
* throw when the container is empty.
|
||||||
|
*/
|
||||||
|
void pop_back() {
|
||||||
|
if (element_count == 0) throw std::exception();
|
||||||
|
erase(iterator(tail));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts an element to the beginning.
|
||||||
|
*/
|
||||||
|
void push_front(const T &value) {
|
||||||
|
insert(begin(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes the first element.
|
||||||
|
* throw when the container is empty.
|
||||||
|
*/
|
||||||
|
void pop_front() {
|
||||||
|
if (element_count == 0) throw std::exception();
|
||||||
|
erase(iterator(head));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace sjtu
|
||||||
|
|
||||||
|
#endif // SJTU_LIST_HPP
|
803
stlite/map.hpp
Normal file
803
stlite/map.hpp
Normal file
@ -0,0 +1,803 @@
|
|||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
#include <iterator>
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <queue> // only for debug use
|
||||||
|
#include <vector> // only for debug use
|
||||||
|
#endif
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
struct map_iterator_tag : std::bidirectional_iterator_tag {};
|
||||||
|
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 sjtu::map_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 sjtu::map_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 begin() const { return cbegin(); }
|
||||||
|
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 end() const { return cend(); }
|
||||||
|
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
|
129
stlite/priority_queue.hpp
Normal file
129
stlite/priority_queue.hpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#ifndef SJTU_PRIORITY_QUEUE_HPP
|
||||||
|
#define SJTU_PRIORITY_QUEUE_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a container like std::priority_queue which is a heap internal.
|
||||||
|
* Use Skew heap to implement this priority_queue.
|
||||||
|
*/
|
||||||
|
template <typename T, class Compare = std::less<T>>
|
||||||
|
class priority_queue {
|
||||||
|
Compare comp;
|
||||||
|
struct Node {
|
||||||
|
T value;
|
||||||
|
Node *left, *right;
|
||||||
|
Node(const T &v) : value(v), left(nullptr), right(nullptr) {}
|
||||||
|
} * root;
|
||||||
|
size_t node_count;
|
||||||
|
void FreeAll(Node *node) {
|
||||||
|
if (node == nullptr) return;
|
||||||
|
FreeAll(node->left);
|
||||||
|
FreeAll(node->right);
|
||||||
|
delete node;
|
||||||
|
}
|
||||||
|
void CopyAll(Node *&dest, Node *src) {
|
||||||
|
if (src == nullptr) return;
|
||||||
|
dest = new Node(src->value);
|
||||||
|
CopyAll(dest->left, src->left);
|
||||||
|
CopyAll(dest->right, src->right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *SkewMerge(Node *a, Node *b) {
|
||||||
|
if (a == nullptr) return b;
|
||||||
|
if (b == nullptr) return a;
|
||||||
|
if (comp(a->value, b->value)) std::swap(a, b);
|
||||||
|
a->right = SkewMerge(a->right, b);
|
||||||
|
std::swap(a->left, a->right);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
priority_queue() : root(nullptr), node_count(0) {}
|
||||||
|
priority_queue(const priority_queue &other) {
|
||||||
|
root = nullptr;
|
||||||
|
CopyAll(root, other.root);
|
||||||
|
node_count = other.node_count;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO deconstructor
|
||||||
|
*/
|
||||||
|
~priority_queue() {
|
||||||
|
FreeAll(root);
|
||||||
|
root = nullptr;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO Assignment operator
|
||||||
|
*/
|
||||||
|
priority_queue &operator=(const priority_queue &other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
FreeAll(root);
|
||||||
|
root = nullptr;
|
||||||
|
CopyAll(root, other.root);
|
||||||
|
node_count = other.node_count;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get the top of the queue.
|
||||||
|
* @return a reference of the top element.
|
||||||
|
* throw container_is_empty if empty() returns true;
|
||||||
|
*/
|
||||||
|
const T &top() const {
|
||||||
|
if (empty()) throw container_is_empty();
|
||||||
|
return root->value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* push new element to the priority queue.
|
||||||
|
*/
|
||||||
|
void push(const T &e) {
|
||||||
|
Node *new_node = new Node(e);
|
||||||
|
try {
|
||||||
|
root = SkewMerge(root, new_node);
|
||||||
|
} catch (...) {
|
||||||
|
delete new_node;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
++node_count;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* delete the top element.
|
||||||
|
* throw container_is_empty if empty() returns true;
|
||||||
|
*/
|
||||||
|
void pop() {
|
||||||
|
if (empty()) throw container_is_empty();
|
||||||
|
Node *old_root = root;
|
||||||
|
root = SkewMerge(root->left, root->right);
|
||||||
|
delete old_root;
|
||||||
|
--node_count;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* return the number of the elements.
|
||||||
|
*/
|
||||||
|
size_t size() const { return node_count; }
|
||||||
|
/**
|
||||||
|
* check if the container has at least an element.
|
||||||
|
* @return true if it is empty, false if it has at least an element.
|
||||||
|
*/
|
||||||
|
bool empty() const { return node_count == 0; }
|
||||||
|
/**
|
||||||
|
* merge two priority_queues with at most O(logn) complexity.
|
||||||
|
* clear the other priority_queue.
|
||||||
|
*/
|
||||||
|
void merge(priority_queue &other) {
|
||||||
|
if (other.root == root) return;
|
||||||
|
root = SkewMerge(root, other.root);
|
||||||
|
node_count += other.node_count;
|
||||||
|
other.root = nullptr;
|
||||||
|
other.node_count = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sjtu
|
||||||
|
|
||||||
|
#endif
|
27
stlite/utility.hpp
Normal file
27
stlite/utility.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef SJTU_UTILITY_HPP
|
||||||
|
#define SJTU_UTILITY_HPP
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
|
||||||
|
template<class T1, class T2>
|
||||||
|
class pair {
|
||||||
|
public:
|
||||||
|
T1 first;
|
||||||
|
T2 second;
|
||||||
|
constexpr pair() : first(), second() {}
|
||||||
|
pair(const pair &other) = default;
|
||||||
|
pair(pair &&other) = default;
|
||||||
|
pair(const T1 &x, const T2 &y) : first(x), second(y) {}
|
||||||
|
template<class U1, class U2>
|
||||||
|
pair(U1 &&x, U2 &&y) : first(x), second(y) {}
|
||||||
|
template<class U1, class U2>
|
||||||
|
pair(const pair<U1, U2> &other) : first(other.first), second(other.second) {}
|
||||||
|
template<class U1, class U2>
|
||||||
|
pair(pair<U1, U2> &&other) : first(other.first), second(other.second) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
543
stlite/vector.hpp
Normal file
543
stlite/vector.hpp
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
#ifndef SJTU_VECTOR_HPP
|
||||||
|
#define SJTU_VECTOR_HPP
|
||||||
|
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <exception>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
/**
|
||||||
|
* a data container like std::vector
|
||||||
|
* store data in a successive memory and support random access.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class vector {
|
||||||
|
static std::allocator<T> alloc;
|
||||||
|
size_t allocated_length;
|
||||||
|
size_t current_length;
|
||||||
|
T *raw_beg, *raw_end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* you can see RandomAccessIterator at CppReference for help.
|
||||||
|
*/
|
||||||
|
class const_iterator;
|
||||||
|
class iterator {
|
||||||
|
// The following code is written for the C++ type_traits library.
|
||||||
|
// Type traits is a C++ feature for describing certain properties of a type.
|
||||||
|
// For instance, for an iterator, iterator::value_type is the type that the
|
||||||
|
// iterator points to.
|
||||||
|
// STL algorithms and containers may use these type_traits (e.g. the following
|
||||||
|
// typedef) to work properly. In particular, without the following code,
|
||||||
|
// @code{std::sort(iter, iter1);} would not compile.
|
||||||
|
// See these websites for more information:
|
||||||
|
// https://en.cppreference.com/w/cpp/header/type_traits
|
||||||
|
// About value_type: https://blog.csdn.net/u014299153/article/details/72419713
|
||||||
|
// About iterator_category: https://en.cppreference.com/w/cpp/iterator
|
||||||
|
friend class vector<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = T;
|
||||||
|
using pointer = T *;
|
||||||
|
using reference = T &;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<T> *domain;
|
||||||
|
T *raw_pointer;
|
||||||
|
iterator(vector<T> *domain, T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* return a new iterator which pointer n-next elements
|
||||||
|
* as well as operator-
|
||||||
|
*/
|
||||||
|
iterator operator+(const int &n) const {
|
||||||
|
iterator temp = *this;
|
||||||
|
temp.raw_pointer += n;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
iterator operator-(const int &n) const {
|
||||||
|
iterator temp = *this;
|
||||||
|
temp.raw_pointer -= n;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
// return the distance between two iterators,
|
||||||
|
// if these two iterators point to different vectors, throw invaild_iterator.
|
||||||
|
int operator-(const iterator &rhs) const {
|
||||||
|
if (domain != rhs.domain) [[unlikely]]
|
||||||
|
throw invalid_iterator();
|
||||||
|
return raw_pointer - rhs.raw_pointer;
|
||||||
|
}
|
||||||
|
iterator &operator+=(const int &n) {
|
||||||
|
raw_pointer += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator &operator-=(const int &n) {
|
||||||
|
raw_pointer -= n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO iter++
|
||||||
|
*/
|
||||||
|
iterator operator++(int) {
|
||||||
|
iterator temp = *this;
|
||||||
|
raw_pointer++;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO ++iter
|
||||||
|
*/
|
||||||
|
iterator &operator++() {
|
||||||
|
raw_pointer++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO iter--
|
||||||
|
*/
|
||||||
|
iterator operator--(int) {
|
||||||
|
iterator temp = *this;
|
||||||
|
raw_pointer--;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO --iter
|
||||||
|
*/
|
||||||
|
iterator &operator--() {
|
||||||
|
raw_pointer--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO *it
|
||||||
|
*/
|
||||||
|
T &operator*() const { return *raw_pointer; }
|
||||||
|
/**
|
||||||
|
* a operator to check whether two iterators are same (pointing to the same memory address).
|
||||||
|
*/
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* has same function as iterator, just for a const object.
|
||||||
|
*/
|
||||||
|
class const_iterator {
|
||||||
|
friend class vector<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = T;
|
||||||
|
using pointer = T *;
|
||||||
|
using reference = T &;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const vector<T> *domain;
|
||||||
|
const T *raw_pointer;
|
||||||
|
inline const_iterator(const vector<T> *domain, const T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* return a new iterator which pointer n-next elements
|
||||||
|
* as well as operator-
|
||||||
|
*/
|
||||||
|
const_iterator operator+(const int &n) const {
|
||||||
|
const_iterator temp = *this;
|
||||||
|
temp.raw_pointer += n;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
const_iterator operator-(const int &n) const {
|
||||||
|
const_iterator temp = *this;
|
||||||
|
temp.raw_pointer -= n;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
// return the distance between two iterators,
|
||||||
|
// if these two iterators point to different vectors, throw invaild_iterator.
|
||||||
|
int operator-(const const_iterator &rhs) const {
|
||||||
|
if (domain != rhs.domain) [[unlikely]]
|
||||||
|
throw invalid_iterator();
|
||||||
|
return raw_pointer - rhs.raw_pointer;
|
||||||
|
}
|
||||||
|
const_iterator &operator+=(const int &n) {
|
||||||
|
raw_pointer += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
const_iterator &operator-=(const int &n) {
|
||||||
|
raw_pointer -= n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO iter++
|
||||||
|
*/
|
||||||
|
const_iterator operator++(int) {
|
||||||
|
const_iterator temp = *this;
|
||||||
|
raw_pointer++;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO ++iter
|
||||||
|
*/
|
||||||
|
const_iterator &operator++() {
|
||||||
|
raw_pointer++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO iter--
|
||||||
|
*/
|
||||||
|
const_iterator operator--(int) {
|
||||||
|
const_iterator temp = *this;
|
||||||
|
raw_pointer--;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO --iter
|
||||||
|
*/
|
||||||
|
const_iterator &operator--() {
|
||||||
|
raw_pointer--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO *it
|
||||||
|
*/
|
||||||
|
const T &operator*() const { return *raw_pointer; }
|
||||||
|
/**
|
||||||
|
* a operator to check whether two iterators are same (pointing to the same memory address).
|
||||||
|
*/
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* TODO Constructs
|
||||||
|
* At least two: default constructor, copy constructor
|
||||||
|
*/
|
||||||
|
vector() {
|
||||||
|
raw_beg = alloc.allocate(1);
|
||||||
|
raw_end = raw_beg;
|
||||||
|
allocated_length = 1;
|
||||||
|
current_length = 0;
|
||||||
|
}
|
||||||
|
vector(const vector &other) {
|
||||||
|
raw_beg = alloc.allocate(other.allocated_length);
|
||||||
|
raw_end = raw_beg + other.current_length;
|
||||||
|
allocated_length = other.allocated_length;
|
||||||
|
current_length = other.current_length;
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, raw_beg + i, other.raw_beg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vector(vector &&other) noexcept {
|
||||||
|
raw_beg = other.raw_beg;
|
||||||
|
raw_end = other.raw_end;
|
||||||
|
allocated_length = other.allocated_length;
|
||||||
|
current_length = other.current_length;
|
||||||
|
other.raw_beg = nullptr;
|
||||||
|
other.raw_end = nullptr;
|
||||||
|
other.allocated_length = 0;
|
||||||
|
other.current_length = 0;
|
||||||
|
}
|
||||||
|
~vector() {
|
||||||
|
if (raw_beg != nullptr) {
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TODO Assignment operator
|
||||||
|
*/
|
||||||
|
vector &operator=(const vector &other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
if (raw_beg != nullptr) {
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
}
|
||||||
|
raw_beg = alloc.allocate(other.allocated_length);
|
||||||
|
raw_end = raw_beg + other.current_length;
|
||||||
|
allocated_length = other.allocated_length;
|
||||||
|
current_length = other.current_length;
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, raw_beg + i, other.raw_beg[i]);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
vector &operator=(vector &&other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
if (raw_beg != nullptr) {
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
}
|
||||||
|
raw_beg = other.raw_beg;
|
||||||
|
raw_end = other.raw_end;
|
||||||
|
allocated_length = other.allocated_length;
|
||||||
|
current_length = other.current_length;
|
||||||
|
other.raw_beg = nullptr;
|
||||||
|
other.raw_end = nullptr;
|
||||||
|
other.allocated_length = 0;
|
||||||
|
other.current_length = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* assigns specified element with bounds checking
|
||||||
|
* throw index_out_of_bound if pos is not in [0, size)
|
||||||
|
*/
|
||||||
|
T &at(const size_t &pos) {
|
||||||
|
if (pos < 0 || pos >= current_length) [[unlikely]]
|
||||||
|
throw index_out_of_bound();
|
||||||
|
return raw_beg[pos];
|
||||||
|
}
|
||||||
|
const T &at(const size_t &pos) const {
|
||||||
|
if (pos < 0 || pos >= current_length) [[unlikely]]
|
||||||
|
throw index_out_of_bound();
|
||||||
|
return raw_beg[pos];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* assigns specified element with bounds checking
|
||||||
|
* throw index_out_of_bound if pos is not in [0, size)
|
||||||
|
* !!! Pay attentions
|
||||||
|
* In STL this operator does not check the boundary but I want you to do.
|
||||||
|
*/
|
||||||
|
T &operator[](const size_t &pos) {
|
||||||
|
if (pos < 0 || pos >= current_length) [[unlikely]]
|
||||||
|
throw index_out_of_bound();
|
||||||
|
return raw_beg[pos];
|
||||||
|
}
|
||||||
|
const T &operator[](const size_t &pos) const {
|
||||||
|
if (pos < 0 || pos >= current_length) [[unlikely]]
|
||||||
|
throw index_out_of_bound();
|
||||||
|
return raw_beg[pos];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* access the first element.
|
||||||
|
* throw container_is_empty if size == 0
|
||||||
|
*/
|
||||||
|
const T &front() const {
|
||||||
|
if (current_length == 0) [[unlikely]]
|
||||||
|
throw container_is_empty();
|
||||||
|
return raw_beg[0];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* access the last element.
|
||||||
|
* throw container_is_empty if size == 0
|
||||||
|
*/
|
||||||
|
const T &back() const {
|
||||||
|
if (current_length == 0) [[unlikely]]
|
||||||
|
throw container_is_empty();
|
||||||
|
return raw_end[-1];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* returns an iterator to the beginning.
|
||||||
|
*/
|
||||||
|
iterator begin() { return iterator(this, raw_beg); }
|
||||||
|
const_iterator begin() const { return const_iterator(this, raw_beg); }
|
||||||
|
const_iterator cbegin() const { return const_iterator(this, raw_beg); }
|
||||||
|
/**
|
||||||
|
* returns an iterator to the end.
|
||||||
|
*/
|
||||||
|
iterator end() { return iterator(this, raw_end); }
|
||||||
|
const_iterator end() const { return const_iterator(this, raw_end); }
|
||||||
|
const_iterator cend() const { return const_iterator(this, raw_end); }
|
||||||
|
/**
|
||||||
|
* checks whether the container is empty
|
||||||
|
*/
|
||||||
|
bool empty() const { return current_length == 0; }
|
||||||
|
/**
|
||||||
|
* returns the number of elements
|
||||||
|
*/
|
||||||
|
size_t size() const { return current_length; }
|
||||||
|
/**
|
||||||
|
* clears the contents
|
||||||
|
*/
|
||||||
|
void clear() {
|
||||||
|
if (raw_beg != nullptr) {
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
}
|
||||||
|
raw_beg = alloc.allocate(1);
|
||||||
|
raw_end = raw_beg;
|
||||||
|
allocated_length = 1;
|
||||||
|
current_length = 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* inserts value before pos
|
||||||
|
* returns an iterator pointing to the inserted value.
|
||||||
|
*/
|
||||||
|
iterator insert(iterator pos, const T &value) {
|
||||||
|
if (pos.raw_pointer < raw_beg || pos.raw_pointer > raw_end) throw invalid_iterator();
|
||||||
|
if (current_length == allocated_length) {
|
||||||
|
size_t new_allocated_length = allocated_length * 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
pos.raw_pointer = new_raw_beg + (pos.raw_pointer - raw_beg);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
for (T *i = raw_end; i != pos.raw_pointer; --i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, i, std::move(*(i - 1)));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, i - 1);
|
||||||
|
}
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, pos.raw_pointer, value);
|
||||||
|
raw_end++;
|
||||||
|
current_length++;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* inserts value at index ind.
|
||||||
|
* after inserting, this->at(ind) == value
|
||||||
|
* returns an iterator pointing to the inserted value.
|
||||||
|
* throw index_out_of_bound if ind > size (in this situation ind can be size because after inserting the size will
|
||||||
|
* increase 1.)
|
||||||
|
*/
|
||||||
|
iterator insert(const size_t &ind, const T &value) {
|
||||||
|
if (ind < 0 || ind > current_length) throw index_out_of_bound();
|
||||||
|
if (current_length == allocated_length) {
|
||||||
|
size_t new_allocated_length = allocated_length * 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
alloc.construct(new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
alloc.destroy(raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
for (T *i = raw_end; i != raw_beg + ind; --i) {
|
||||||
|
alloc.construct(i, std::move(*(i - 1)));
|
||||||
|
alloc.destroy(i - 1);
|
||||||
|
}
|
||||||
|
alloc.construct(raw_beg + ind, value);
|
||||||
|
raw_end++;
|
||||||
|
current_length++;
|
||||||
|
return iterator(this, raw_beg + ind);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* removes the element at pos.
|
||||||
|
* return an iterator pointing to the following element.
|
||||||
|
* If the iterator pos refers the last element, the end() iterator is returned.
|
||||||
|
*/
|
||||||
|
iterator erase(iterator pos) {
|
||||||
|
if (pos.raw_pointer < raw_beg || pos.raw_pointer >= raw_end) throw invalid_iterator();
|
||||||
|
for (T *i = pos.raw_pointer; i != raw_end - 1; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, i, std::move(*(i + 1)));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, i + 1);
|
||||||
|
}
|
||||||
|
raw_end--;
|
||||||
|
current_length--;
|
||||||
|
if (current_length != 0 && current_length <= allocated_length / 4) {
|
||||||
|
size_t new_allocated_length = allocated_length / 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* removes the element with index ind.
|
||||||
|
* return an iterator pointing to the following element.
|
||||||
|
* throw index_out_of_bound if ind >= size
|
||||||
|
*/
|
||||||
|
iterator erase(const size_t &ind) {
|
||||||
|
if (ind < 0 || ind >= current_length) throw index_out_of_bound();
|
||||||
|
for (T *i = raw_beg + ind; i != raw_end - 1; ++i) {
|
||||||
|
alloc.construct(i, std::move(*(i + 1)));
|
||||||
|
alloc.destroy(i + 1);
|
||||||
|
}
|
||||||
|
raw_end--;
|
||||||
|
current_length--;
|
||||||
|
if (current_length != 0 && current_length <= allocated_length / 4) {
|
||||||
|
size_t new_allocated_length = allocated_length / 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
alloc.construct(new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
alloc.destroy(raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
return iterator(this, raw_beg + ind);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* adds an element to the end.
|
||||||
|
*/
|
||||||
|
void push_back(const T &value) {
|
||||||
|
if (current_length == allocated_length) [[unlikely]] {
|
||||||
|
size_t new_allocated_length = allocated_length * 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, raw_end, value);
|
||||||
|
raw_end++;
|
||||||
|
current_length++;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* remove the last element from the end.
|
||||||
|
* throw container_is_empty if size() == 0
|
||||||
|
*/
|
||||||
|
void pop_back() {
|
||||||
|
if (current_length == 0) [[unlikely]]
|
||||||
|
throw container_is_empty();
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_end - 1);
|
||||||
|
raw_end--;
|
||||||
|
current_length--;
|
||||||
|
if (current_length != 0 && current_length <= allocated_length / 4) [[unlikely]] {
|
||||||
|
size_t new_allocated_length = allocated_length / 2;
|
||||||
|
T *new_raw_beg = alloc.allocate(new_allocated_length);
|
||||||
|
for (size_t i = 0; i < current_length; ++i) {
|
||||||
|
std::allocator_traits<decltype(alloc)>::construct(alloc, new_raw_beg + i, std::move(raw_beg[i]));
|
||||||
|
std::allocator_traits<decltype(alloc)>::destroy(alloc, raw_beg + i);
|
||||||
|
}
|
||||||
|
alloc.deallocate(raw_beg, allocated_length);
|
||||||
|
raw_beg = new_raw_beg;
|
||||||
|
raw_end = raw_beg + current_length;
|
||||||
|
allocated_length = new_allocated_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
std::allocator<T> vector<T>::alloc;
|
||||||
|
|
||||||
|
} // namespace sjtu
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user