replaced array based binary tree for pointer based binary tree and got a draft of insertion done and a good chunk of a draft for removal done
This commit is contained in:
parent
c47ee41caf
commit
2f323d8561
1 changed files with 416 additions and 194 deletions
610
Map.hpp
610
Map.hpp
|
@ -1,8 +1,8 @@
|
||||||
// commenting everything out when I commit so all commits my code technically
|
// commenting everything out when I commit so all commits my code technically
|
||||||
// compiles
|
// compiles
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <deque>
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -18,27 +18,84 @@ template <typename Key_T, typename Mapped_T> class Map;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
enum class Color { Red, Black };
|
enum class Color { Red, Black };
|
||||||
|
enum class Direction { Left, Right };
|
||||||
|
Direction operator!(Direction dir) {
|
||||||
|
switch (dir) {
|
||||||
|
case Direction::Left:
|
||||||
|
return Direction::Right;
|
||||||
|
case Direction::Right:
|
||||||
|
return Direction::Left;
|
||||||
|
default:
|
||||||
|
// unreachable the only directions are left and right
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
template <typename Key_T, typename Mapped_T> struct BookKeeping {
|
template <typename Key_T, typename Mapped_T> struct BookKeeping {
|
||||||
friend class Map<Key_T, Mapped_T>;
|
using Self = BookKeeping<Key_T, Mapped_T>;
|
||||||
using ValueType = std::pair<const Key_T, Mapped_T>;
|
using ValueType = std::pair<const Key_T, Mapped_T>;
|
||||||
Map<Key_T, Mapped_T> &parent;
|
friend class Map<Key_T, Mapped_T>;
|
||||||
|
Map<Key_T, Mapped_T> &container;
|
||||||
ValueType value;
|
ValueType value;
|
||||||
std::size_t self;
|
|
||||||
Color color;
|
Color color;
|
||||||
|
// nullptr indicates empty
|
||||||
|
Self *parent;
|
||||||
|
Self *left;
|
||||||
|
Self *right;
|
||||||
|
Self *prev;
|
||||||
|
Self *next;
|
||||||
|
// reference to a pointer because the alternatives were worse
|
||||||
|
inline Self *&child(Direction dir) {
|
||||||
|
switch (dir) {
|
||||||
|
case Direction::Left:
|
||||||
|
return left;
|
||||||
|
break;
|
||||||
|
case Direction::Right:
|
||||||
|
return right;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// this is root/P for this method
|
||||||
|
// copying from wikipedia RotateDirRoot with translation into my own idioms
|
||||||
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Operations
|
||||||
|
inline void rotate(Direction dir) {
|
||||||
|
// wikipedia version uses alphabet soup, might fix later
|
||||||
|
Self *P = this;
|
||||||
|
auto &T = container;
|
||||||
|
Self *G = P->parent;
|
||||||
|
Self *S = P->child(!dir);
|
||||||
|
Self *C;
|
||||||
|
|
||||||
std::optional<std::size_t> next;
|
// this method shouldn't be called in cases where this assert will trip
|
||||||
std::optional<std::size_t> prev;
|
assert(S != nullptr);
|
||||||
|
|
||||||
|
//
|
||||||
|
C = S->child(dir);
|
||||||
|
P->child(!dir) = C;
|
||||||
|
|
||||||
|
if (C != nullptr) {
|
||||||
|
C->parent = P;
|
||||||
|
}
|
||||||
|
S->child(dir) = P;
|
||||||
|
P->parent = S;
|
||||||
|
S->parent = G;
|
||||||
|
if (G != nullptr) {
|
||||||
|
if (P == G->right) {
|
||||||
|
G->right = S;
|
||||||
|
} else {
|
||||||
|
G->left = S;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
T->root = S;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||||||
template <typename Key_T, typename Mapped_T> class Map {
|
template <typename Key_T, typename Mapped_T> class Map {
|
||||||
private:
|
private:
|
||||||
using ValueType = std::pair<const Key_T, Mapped_T>;
|
using ValueType = std::pair<const Key_T, Mapped_T>;
|
||||||
// idx 0 = root
|
using Node = BookKeeping<Key_T, Mapped_T>;
|
||||||
// left = parent * 2 + 1
|
using Map_T = Map<Key_T, Mapped_T>;
|
||||||
// right = parent * 2 + 2
|
|
||||||
std::vector<std::optional<BookKeeping<Key_T, Mapped_T>>> store;
|
|
||||||
std::vector<Color> coloration;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Iterator;
|
class Iterator;
|
||||||
|
@ -48,59 +105,70 @@ public:
|
||||||
friend class Iterator;
|
friend class Iterator;
|
||||||
friend class ConstIterator;
|
friend class ConstIterator;
|
||||||
friend class ReverseIterator;
|
friend class ReverseIterator;
|
||||||
friend struct BookKeeping<Key_T, Mapped_T>;
|
friend Node;
|
||||||
// TODO: Iterator functionality
|
|
||||||
class Iterator {
|
class Iterator {
|
||||||
friend class Map<Key_T, Mapped_T>;
|
friend Map_T;
|
||||||
friend struct BookKeeping<Key_T, Mapped_T>;
|
friend Node;
|
||||||
|
|
||||||
public:
|
|
||||||
private:
|
private:
|
||||||
enum class PastElem { start, end, neither };
|
|
||||||
// pointer needed so we can replace as needed
|
// pointer needed so we can replace as needed
|
||||||
BookKeeping<Key_T, Mapped_T> *parent;
|
Node *ref;
|
||||||
|
Node *escape;
|
||||||
// TODO: next/prev found in bookkeeping
|
Iterator(Node *ref, Node *escape = nullptr) : ref{ref}, escape{escape} {}
|
||||||
// Note: only used when past first/last element
|
|
||||||
PastElem use;
|
|
||||||
std::optional<std::size_t> next_or_prev;
|
|
||||||
Iterator(std::optional<BookKeeping<Key_T, Mapped_T>> &parent)
|
|
||||||
: parent{&parent}, use{PastElem::neither}, next_or_prev{std::nullopt} {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Iterator() = delete;
|
Iterator() = delete;
|
||||||
ConstIterator to_const() const { return ConstIterator(*this); }
|
Iterator &operator++() {
|
||||||
Iterator &operator++() { return *this; }
|
if (ref == nullptr) {
|
||||||
|
ref = escape;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
if (ref->next == nullptr) {
|
||||||
|
escape = ref;
|
||||||
|
}
|
||||||
|
ref = ref->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
Iterator operator++(int) {
|
Iterator operator++(int) {
|
||||||
Iterator tmp = *this;
|
Iterator tmp = *this;
|
||||||
++(*this);
|
++(*this);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
Iterator &operator--() { return *this; }
|
Iterator &operator--() {
|
||||||
|
if (ref == nullptr) {
|
||||||
|
ref = escape;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
if (ref->prev == nullptr) {
|
||||||
|
escape = ref;
|
||||||
|
}
|
||||||
|
ref = ref->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
Iterator operator--(int) {
|
Iterator operator--(int) {
|
||||||
Iterator tmp = *this;
|
Iterator tmp = *this;
|
||||||
--(*this);
|
--(*this);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
ValueType &operator*() const { return parent.parent.at(parent.self); }
|
ValueType &operator*() const { return this->ref->value; }
|
||||||
ValueType *operator->() const { return &**this; }
|
ValueType *operator->() const { return &this->ref->value; }
|
||||||
friend bool operator==(Iterator const &lhs, Iterator const &rhs) {
|
friend bool operator==(Iterator const &lhs, Iterator const &rhs) {
|
||||||
return lhs.store_iter == rhs.store_iter;
|
return lhs.ref == rhs.ref;
|
||||||
}
|
}
|
||||||
friend bool operator!=(Iterator const &lhs, Iterator const &rhs) {
|
friend bool operator!=(Iterator const &lhs, Iterator const &rhs) {
|
||||||
return lhs.store_iter != rhs.store_iter;
|
return lhs.ref != rhs.ref;
|
||||||
}
|
}
|
||||||
friend bool operator==(ConstIterator const &lhs, Iterator const &rhs) {
|
friend bool operator==(ConstIterator const &lhs, Iterator const &rhs) {
|
||||||
return lhs == rhs.to_const();
|
return lhs.store_iter.ref == rhs.ref;
|
||||||
}
|
}
|
||||||
friend bool operator!=(ConstIterator const &lhs, Iterator const &rhs) {
|
friend bool operator!=(ConstIterator const &lhs, Iterator const &rhs) {
|
||||||
return lhs != rhs.to_const();
|
return lhs.store_iter.ref != rhs.ref;
|
||||||
}
|
}
|
||||||
friend bool operator==(Iterator const &lhs, ConstIterator const &rhs) {
|
friend bool operator==(Iterator const &lhs, ConstIterator const &rhs) {
|
||||||
return lhs.to_const() == rhs;
|
return lhs.ref == rhs.store_iter.ref;
|
||||||
}
|
}
|
||||||
friend bool operator!=(Iterator const &lhs, ConstIterator const &rhs) {
|
friend bool operator!=(Iterator const &lhs, ConstIterator const &rhs) {
|
||||||
return lhs.to_const() != rhs;
|
return lhs.ref != rhs.store_iter.ref;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class ConstIterator {
|
class ConstIterator {
|
||||||
|
@ -138,7 +206,6 @@ public:
|
||||||
}
|
}
|
||||||
const ValueType &operator*() const { return *this->store_iter; }
|
const ValueType &operator*() const { return *this->store_iter; }
|
||||||
const ValueType *operator->() const {
|
const ValueType *operator->() const {
|
||||||
// I find this rather funny
|
|
||||||
return this->store_iter.operator->();
|
return this->store_iter.operator->();
|
||||||
}
|
}
|
||||||
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
||||||
|
@ -148,7 +215,8 @@ public:
|
||||||
class ReverseIterator {
|
class ReverseIterator {
|
||||||
public:
|
public:
|
||||||
friend class Map<Key_T, Mapped_T>;
|
friend class Map<Key_T, Mapped_T>;
|
||||||
using underlying = typename std::vector<std::optional<ValueType>>::iterator;
|
friend class Iterator;
|
||||||
|
using underlying = Iterator;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
underlying store_iter;
|
underlying store_iter;
|
||||||
|
@ -156,12 +224,26 @@ public:
|
||||||
public:
|
public:
|
||||||
ReverseIterator() = delete;
|
ReverseIterator() = delete;
|
||||||
ReverseIterator(underlying store_iter) : store_iter{store_iter} {}
|
ReverseIterator(underlying store_iter) : store_iter{store_iter} {}
|
||||||
ReverseIterator &operator++() {}
|
ReverseIterator &operator++() {
|
||||||
ReverseIterator operator++(int) {}
|
--store_iter;
|
||||||
ReverseIterator &operator--() {}
|
return *this;
|
||||||
ReverseIterator operator--(int) {}
|
}
|
||||||
ValueType &operator*() const {}
|
ReverseIterator operator++(int) {
|
||||||
ValueType *operator->() const {}
|
ReverseIterator ret = *this;
|
||||||
|
++(*this);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ReverseIterator &operator--() {
|
||||||
|
++store_iter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ReverseIterator operator--(int) {
|
||||||
|
ReverseIterator ret = *this;
|
||||||
|
--(*this);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ValueType &operator*() const { return this->store_iter.ref->value; }
|
||||||
|
ValueType *operator->() const { return &this->store_iter.ref->value; }
|
||||||
friend bool operator==(ReverseIterator const &lhs,
|
friend bool operator==(ReverseIterator const &lhs,
|
||||||
ReverseIterator const &rhs) {
|
ReverseIterator const &rhs) {
|
||||||
return lhs.store_iter == rhs.store_iter;
|
return lhs.store_iter == rhs.store_iter;
|
||||||
|
@ -170,155 +252,328 @@ public:
|
||||||
return lhs.store_iter != rhs.store_iter;
|
return lhs.store_iter != rhs.store_iter;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Map() : store{} {}
|
|
||||||
Map(const Map &rhs) : store{rhs.store} {}
|
private:
|
||||||
Map &operator=(const Map &rhs) { this->store = rhs.store; }
|
Node *root;
|
||||||
Map(std::initializer_list<ValueType> elems) : store{} {
|
Node *min;
|
||||||
|
Node *max;
|
||||||
|
std::vector<Node> nodes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Map() : root{nullptr}, min{nullptr}, max{nullptr}, nodes{} {}
|
||||||
|
Map(const Map &rhs)
|
||||||
|
: root{rhs.root}, min{nullptr}, max{nullptr}, nodes{rhs.nodes} {}
|
||||||
|
Map &operator=(const Map &rhs) {
|
||||||
|
this->root = rhs.root;
|
||||||
|
this->min = rhs.min;
|
||||||
|
this->max = rhs.max;
|
||||||
|
this->nodes = rhs.nodes;
|
||||||
|
}
|
||||||
|
Map(std::initializer_list<ValueType> elems) : root{nullptr}, nodes{} {
|
||||||
this->insert(elems.begin(), elems.end());
|
this->insert(elems.begin(), elems.end());
|
||||||
}
|
}
|
||||||
// who cares we're using vector
|
|
||||||
~Map() {}
|
~Map() {}
|
||||||
|
|
||||||
size_t size() const {
|
size_t size() const {
|
||||||
std::size_t count = 0;
|
root = nullptr;
|
||||||
for (auto &m_pair : this->store) {
|
return this->nodes.size();
|
||||||
count += m_pair.has_value() ? 1 : 0;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
bool empty() const { return this->store.empty(); }
|
bool empty() const { return this->size() == 0; }
|
||||||
// TODO: Iterator creation
|
Iterator begin() { return Iterator{min}; }
|
||||||
Iterator begin() {}
|
Iterator end() { return Iterator{nullptr, max}; }
|
||||||
Iterator end() {}
|
ConstIterator begin() const { return ConstIterator{this->begin()}; }
|
||||||
ConstIterator begin() const {}
|
ConstIterator end() const { return ConstIterator{this->end()}; }
|
||||||
ConstIterator end() const {}
|
ConstIterator cbegin() const { return this->begin(); }
|
||||||
ConstIterator cbegin() const {}
|
ConstIterator cend() const { return this->end(); }
|
||||||
ConstIterator cend() const {}
|
ReverseIterator rbegin() { return ReverseIterator{Iterator{this->max}}; }
|
||||||
ReverseIterator rbegin() {}
|
ReverseIterator rend() { return ReverseIterator{Iterator{nullptr, min}}; }
|
||||||
ReverseIterator rend() {}
|
|
||||||
// TODO: actually return an iterator from find and deal with error cases
|
|
||||||
// correctly, also need to update for new bookkeeping type
|
|
||||||
Iterator find(const Key_T &key) {
|
Iterator find(const Key_T &key) {
|
||||||
std::size_t idx = 0;
|
// we need a locate slot function for insert regardless so might as well use
|
||||||
while (store[idx].first != key) {
|
// it here
|
||||||
if (idx >= store.size()) {
|
auto [parent, dir] = this->locate_slot(key);
|
||||||
return this->end();
|
if (parent == nullptr) {
|
||||||
}
|
return this->end();
|
||||||
if (store[idx].first < key) {
|
|
||||||
idx = idx * 2 + 1;
|
|
||||||
} else {
|
|
||||||
idx = idx * 2 + 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (parent->child(dir) == nullptr) {
|
||||||
ConstIterator find(const Key_T &key) const {
|
return this->end();
|
||||||
std::size_t idx = 0;
|
|
||||||
while (store[idx].first != key) {
|
|
||||||
if (idx >= store.size()) {
|
|
||||||
return this->end();
|
|
||||||
}
|
|
||||||
if (store[idx].first < key) {
|
|
||||||
idx = idx * 2 + 1;
|
|
||||||
} else {
|
|
||||||
idx = idx * 2 + 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return Iterator{parent->child(dir)};
|
||||||
}
|
}
|
||||||
|
// implicit cast to ConstIterator from Iterator
|
||||||
|
ConstIterator find(const Key_T &key) const { return this->find(key); }
|
||||||
|
|
||||||
Mapped_T &at(const Key_T &key) {
|
Mapped_T &at(const Key_T &key) { return (this->find(key))->second; }
|
||||||
std::size_t i = 0;
|
|
||||||
while (this->store.at(i).has_value()) {
|
|
||||||
switch (true) {
|
|
||||||
case this->store.at(i).first == key:
|
|
||||||
return this->store.at(i).second;
|
|
||||||
case this->store.at(i).first < key:
|
|
||||||
i = 2 * i + 1;
|
|
||||||
break;
|
|
||||||
case this->store.at(i).first > key:
|
|
||||||
i = 2 * i + 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw std::out_of_range{""};
|
|
||||||
}
|
|
||||||
const Mapped_T &at(const Key_T &key) const { return this->at(key); }
|
const Mapped_T &at(const Key_T &key) const { return this->at(key); }
|
||||||
Mapped_T &operator[](const Key_T &key) { return this->at(key); }
|
Mapped_T &operator[](const Key_T &key) { return this->at(key); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Color getColor(std::size_t i) {
|
void handle_root_rotation(Node *grandparent, Node *parent, Node *inserting,
|
||||||
if (this->store.size() <= i) {
|
Direction dir) {
|
||||||
return Color::Black;
|
// making inner grandchild into outer grandchild
|
||||||
|
if (inserting == parent->child(!dir)) {
|
||||||
|
parent->rotate(dir);
|
||||||
|
inserting = parent;
|
||||||
|
parent = grandparent->child(dir);
|
||||||
}
|
}
|
||||||
if (!this->store.at(i).has_value()) {
|
// RotateDirRoot(T,G,1-dir);
|
||||||
return Color::Black;
|
Node *gr_grandparent = grandparent->parent;
|
||||||
}
|
Node *sibling = grandparent->child(!dir);
|
||||||
return this->store.at(i).value().color;
|
assert(sibling != nullptr);
|
||||||
}
|
Node *child = sibling->child(dir);
|
||||||
std::size_t find_null(const Key_T &key) {
|
grandparent->child(!dir) = child;
|
||||||
std::size_t idx = 0;
|
sibling->child(dir) = grandparent;
|
||||||
while (store[idx].first != key) {
|
grandparent->parent = sibling;
|
||||||
if (idx >= store.size()) {
|
sibling->parent = gr_grandparent;
|
||||||
return this->end();
|
if (gr_grandparent != nullptr) {
|
||||||
}
|
Direction grandparent_direction;
|
||||||
if (store[idx].first < key) {
|
if (gr_grandparent->left == grandparent) {
|
||||||
if (idx * 2 + 1 > store.size() || !store.at(idx * 2 + 1).has_value()) {
|
grandparent_direction = Direction::Left;
|
||||||
idx = idx * 2 + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
idx = idx * 2 + 1;
|
|
||||||
} else {
|
} else {
|
||||||
if (idx * 2 + 2 > store.size() || !store.at(idx * 2 + 2).has_value()) {
|
grandparent_direction = Direction::Right;
|
||||||
idx = idx * 2 + 2;
|
}
|
||||||
break;
|
gr_grandparent->child(grandparent_direction) = sibling;
|
||||||
}
|
} else {
|
||||||
idx = idx * 2 + 2;
|
this->root = sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->color = Color::Black;
|
||||||
|
grandparent->color = Color::Red;
|
||||||
|
}
|
||||||
|
// heavily referencing the wikipedia implementation for this
|
||||||
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
|
||||||
|
void insert_helper(Node *to_insert, Node *parent, Direction dir) {
|
||||||
|
// initialize the element we're inserting
|
||||||
|
to_insert->color = Color::Red;
|
||||||
|
to_insert->left = nullptr;
|
||||||
|
to_insert->right = nullptr;
|
||||||
|
to_insert->parent = parent;
|
||||||
|
switch (dir) {
|
||||||
|
case Direction::Left:
|
||||||
|
to_insert->next = parent;
|
||||||
|
to_insert->prev = parent->prev;
|
||||||
|
parent->prev = to_insert;
|
||||||
|
break;
|
||||||
|
case Direction::Right:
|
||||||
|
to_insert->prev = parent;
|
||||||
|
to_insert->next = parent->next;
|
||||||
|
parent->next = to_insert;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is the first element to be inserted it's root
|
||||||
|
if (to_insert->parent == nullptr) {
|
||||||
|
this->root = to_insert;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
|
case Direction::Left:
|
||||||
|
parent->left = to_insert;
|
||||||
|
break;
|
||||||
|
case Direction::Right:
|
||||||
|
parent->right = to_insert;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
// don't need to keep track of these in between loops they get
|
||||||
|
// recalculated
|
||||||
|
Node *grandparent;
|
||||||
|
Node *uncle;
|
||||||
|
if (parent->color == Color::Black) {
|
||||||
|
// black parent means invariants definitely hold
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grandparent = parent->parent;
|
||||||
|
|
||||||
|
if (grandparent == nullptr) {
|
||||||
|
// parent is root, just need to recolor it to black
|
||||||
|
parent->color = Color::Black;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction parent_direction;
|
||||||
|
if (grandparent->left == parent) {
|
||||||
|
parent_direction = Direction::Left;
|
||||||
|
uncle = grandparent->right;
|
||||||
|
} else {
|
||||||
|
parent_direction = Direction::Right;
|
||||||
|
uncle = grandparent->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uncle == nullptr || uncle->color == Color::Black) {
|
||||||
|
// case 5 and 6
|
||||||
|
this->handle_root_rotation(grandparent, parent, to_insert,
|
||||||
|
parent_direction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we know parent and uncle are both red so red-black coloring can be
|
||||||
|
// pushed down from grandparent
|
||||||
|
parent->color = Color::Black;
|
||||||
|
uncle->color = Color::Black;
|
||||||
|
grandparent->color = Color::Red;
|
||||||
|
|
||||||
|
to_insert = grandparent;
|
||||||
|
parent = to_insert->parent;
|
||||||
|
} while (parent != nullptr);
|
||||||
|
|
||||||
|
// case 3: current node is red root so we're done
|
||||||
|
}
|
||||||
|
// returns nullptr iff map is empty
|
||||||
|
std::pair<Node *, Direction> locate_slot(const Key_T &key) {
|
||||||
|
Node *current = this->root;
|
||||||
|
Node *parent = nullptr;
|
||||||
|
Direction dir;
|
||||||
|
while (current != nullptr && current->value.first != key) {
|
||||||
|
parent = current;
|
||||||
|
if (current->value.fist < key) {
|
||||||
|
dir = Direction::Left;
|
||||||
|
current = current->left;
|
||||||
|
} else {
|
||||||
|
dir = Direction::Right;
|
||||||
|
current = current->right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return idx;
|
return std::make_pair(parent, dir);
|
||||||
}
|
|
||||||
enum class Direction { left, right };
|
|
||||||
void insert_helper(std::size_t idx, BookKeeping<Key_T, Mapped_T> to_insert) {
|
|
||||||
// might as well make sure
|
|
||||||
to_insert.color = Color::Red;
|
|
||||||
std::size_t parent_idx;
|
|
||||||
Direction relation;
|
|
||||||
if (idx % 2 == 1) {
|
|
||||||
parent_idx = (idx - 1) / 2;
|
|
||||||
relation = Direction::left;
|
|
||||||
} else {
|
|
||||||
parent_idx = (idx - 1) / 2;
|
|
||||||
relation = Direction::right;
|
|
||||||
}
|
|
||||||
BookKeeping<Key_T, Mapped_T> &parent = this->store.at(parent_idx).value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// TODO: single insert
|
// If the key does not already exist in the map, it returns an iterator
|
||||||
// OH NO IT SHOULD BE RED-BLACK
|
// pointing to the new element, and true. If the key already exists, no
|
||||||
|
// insertion is performed nor is the mapped object changed, and it returns
|
||||||
|
// an iterator pointing to the element with the same key, and false.
|
||||||
std::pair<Iterator, bool> insert(const ValueType &val) {
|
std::pair<Iterator, bool> insert(const ValueType &val) {
|
||||||
|
auto [parent, dir] = locate_slot(val.first);
|
||||||
BookKeeping<Key_T, Mapped_T> new_node;
|
bool ret = parent->child(dir) == nullptr;
|
||||||
new_node.color = Color::Red;
|
if (!ret) {
|
||||||
new_node.value = val;
|
return std::make_pair(Iterator{parent->child(dir)}, ret);
|
||||||
new_node.parent = *this;
|
|
||||||
|
|
||||||
if (this->store.size() == 0) {
|
|
||||||
new_node.self = 0;
|
|
||||||
this->store.push_back(new_node);
|
|
||||||
}
|
}
|
||||||
|
Node to_insert;
|
||||||
|
to_insert.value = val;
|
||||||
|
this->nodes.push_back(std::move(to_insert));
|
||||||
|
insert_helper(&nodes.back(), parent, dir);
|
||||||
|
|
||||||
|
if (min == nullptr || val.first < min->value.first) {
|
||||||
|
min = &nodes.back();
|
||||||
|
}
|
||||||
|
if (max == nullptr || val.first > max->value.first) {
|
||||||
|
max = &nodes.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(Iterator{&nodes.back()}, ret);
|
||||||
}
|
}
|
||||||
template <typename IT_T> void insert(IT_T range_beg, IT_T range_end) {
|
template <typename IT_T> void insert(IT_T range_beg, IT_T range_end) {
|
||||||
std::for_each(range_beg, range_end,
|
std::for_each(range_beg, range_end,
|
||||||
[&](ValueType &val) { this->insert(val); });
|
[&](ValueType &val) { this->insert(val); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void case5(Node *parent, Node *sibling, Node *close_nephew,
|
||||||
|
Node *distant_nephew, Direction dir) {
|
||||||
|
sibling->rotate(!dir);
|
||||||
|
sibling->color = Color::Red;
|
||||||
|
close_nephew->color = Color::Black;
|
||||||
|
distant_nephew = sibling;
|
||||||
|
sibling = close_nephew;
|
||||||
|
case6(parent, sibling, distant_nephew, dir);
|
||||||
|
}
|
||||||
|
void case6(Node *parent, Node *sibling, Node *distant_nephew, Direction dir) {
|
||||||
|
parent->rotate(dir);
|
||||||
|
sibling->color = parent->color;
|
||||||
|
parent->color = Color::Black;
|
||||||
|
distant_nephew->color = Color::Black;
|
||||||
|
}
|
||||||
|
// heavily referring to
|
||||||
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal_of_a_black_non-root_leaf
|
||||||
|
void complex_erase(Iterator pos) {
|
||||||
|
|
||||||
|
Node *to_delete = pos.ref;
|
||||||
|
Node *parent = to_delete->parent;
|
||||||
|
assert(parent != nullptr);
|
||||||
|
|
||||||
|
Direction dir =
|
||||||
|
parent->right == to_delete ? Direction::Right : Direction::Left;
|
||||||
|
|
||||||
|
Node *sibling;
|
||||||
|
;
|
||||||
|
Node *close_nephew;
|
||||||
|
Node *distant_nephew;
|
||||||
|
|
||||||
|
parent->child(dir) = nullptr;
|
||||||
|
|
||||||
|
do {
|
||||||
|
dir = parent->right == to_delete ? Direction::Right : Direction::Left;
|
||||||
|
|
||||||
|
sibling = parent->child(!dir);
|
||||||
|
distant_nephew = sibling->child(!dir);
|
||||||
|
close_nephew = sibling->child(dir);
|
||||||
|
|
||||||
|
if (sibling->color == Color::Red) {
|
||||||
|
// case 3
|
||||||
|
parent->rotate(dir);
|
||||||
|
parent->color = Color::Red;
|
||||||
|
sibling->color = Color::Black;
|
||||||
|
sibling = close_nephew;
|
||||||
|
// redundant?
|
||||||
|
distant_nephew = sibling->child(!dir);
|
||||||
|
if (distant_nephew != nullptr && distant_nephew->color == Color::Red) {
|
||||||
|
case6(parent, sibling, distant_nephew, dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close_nephew = sibling->child(dir);
|
||||||
|
if (close_nephew != nullptr && close_nephew->color == Color::Red) {
|
||||||
|
case5(parent, sibling, close_nephew, distant_nephew, dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sibling->color = Color::Red;
|
||||||
|
parent->color = Color::Black;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distant_nephew != nullptr && distant_nephew->color == Color::Red) {
|
||||||
|
case6(parent, sibling, distant_nephew, dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close_nephew != nullptr && close_nephew->color == Color::Red) {
|
||||||
|
case5(parent, sibling, close_nephew, distant_nephew, dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent->color == Color::Red) {
|
||||||
|
// case 4
|
||||||
|
sibling->color = Color::Red;
|
||||||
|
parent->color = Color::Black;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2
|
||||||
|
sibling->color = Color::Red;
|
||||||
|
to_delete = parent;
|
||||||
|
parent = to_delete->parent;
|
||||||
|
} while (parent != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
// TODO: erase via iterator
|
// TODO: erase via iterator
|
||||||
void erase(Iterator pos) {
|
void erase(Iterator pos) {
|
||||||
// RED BLACK TREE oh no
|
// simple cases
|
||||||
|
Node *ref = pos.ref;
|
||||||
|
|
||||||
|
// 2 children just copy over the in order successor and remove successor
|
||||||
|
|
||||||
|
this->complex_erase(pos);
|
||||||
}
|
}
|
||||||
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
||||||
void clear() { this->store = {}; }
|
void clear() {
|
||||||
|
this->root = nullptr;
|
||||||
|
this->nodes.clear();
|
||||||
|
}
|
||||||
friend bool operator==(const Map &lhs, const Map &rhs) {
|
friend bool operator==(const Map &lhs, const Map &rhs) {
|
||||||
if (lhs.store.size() != rhs.store.size()) {
|
if (lhs.nodes.size() != rhs.nodes.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto liter = lhs.cbegin();
|
auto liter = lhs.cbegin();
|
||||||
|
@ -336,40 +591,7 @@ public:
|
||||||
friend bool operator!=(const Map &lhs, const Map &rhs) {
|
friend bool operator!=(const Map &lhs, const Map &rhs) {
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
friend bool operator<(const Map &lhs, const Map &rhs) {
|
// TODO
|
||||||
std::size_t lhs_i = 0;
|
friend bool operator<(const Map &lhs, const Map &rhs) { return false; }
|
||||||
std::size_t rhs_i = 0;
|
|
||||||
for (; lhs_i < lhs.store.size() && rhs_i < rhs.store.size();
|
|
||||||
lhs_i++, rhs_i++) {
|
|
||||||
bool lhs_exhaust = false;
|
|
||||||
while (!lhs.store[lhs_i].has_value()) {
|
|
||||||
lhs_i++;
|
|
||||||
if (lhs.store.size() >= lhs_i) {
|
|
||||||
lhs_exhaust = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rhs_exhaust = false;
|
|
||||||
while (!rhs.store[rhs_i].has_value()) {
|
|
||||||
rhs_i++;
|
|
||||||
if (rhs.store.size() >= rhs_i) {
|
|
||||||
rhs_exhaust = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs_exhaust && !rhs_exhaust) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (lhs_exhaust || rhs_exhaust) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (lhs.store[lhs_i] != rhs.store[rhs_i]) {
|
|
||||||
return lhs.store[lhs_i] < rhs.store[rhs_i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace cs440
|
} // namespace cs440
|
||||||
|
|
Loading…
Reference in a new issue