most of red-black insert done debugging it atm
This commit is contained in:
parent
d8278a195a
commit
9a066ce16f
2 changed files with 123 additions and 791 deletions
146
Map.hpp
146
Map.hpp
|
@ -35,23 +35,32 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
Color color;
|
Color color;
|
||||||
Node *prev;
|
Node *prev;
|
||||||
Node *next;
|
Node *next;
|
||||||
Node(internal_ValueType val)
|
Map *map;
|
||||||
|
Node(internal_ValueType val, Map *map)
|
||||||
: parent{nullptr}, val{val}, left{}, right{}, color{Color::Red},
|
: parent{nullptr}, val{val}, left{}, right{}, color{Color::Red},
|
||||||
prev{nullptr}, next{nullptr} {}
|
prev{nullptr}, next{nullptr}, map{map} {}
|
||||||
Node(const Node &rhs)
|
Node(const Node &rhs)
|
||||||
: parent{nullptr}, val{rhs.val},
|
: parent{nullptr}, val{rhs.val},
|
||||||
left{std::make_unique<Node>(*rhs.left)},
|
left{std::make_unique<Node>(*rhs.left)},
|
||||||
right{std::make_unique<Node>(*rhs.right)}, color{rhs.color},
|
right{std::make_unique<Node>(*rhs.right)}, color{rhs.color},
|
||||||
prev{nullptr}, next{nullptr} {
|
prev{nullptr}, next{nullptr}, map{rhs.map} {
|
||||||
this->left->parent = this;
|
if (this->left) {
|
||||||
this->right->parent = this;
|
this->left->parent = this;
|
||||||
|
}
|
||||||
|
if (this->right) {
|
||||||
|
this->right->parent = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Node(Node &&rhs)
|
Node(Node &&rhs)
|
||||||
: parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)},
|
: parent{nullptr}, val{std::move(rhs.val)}, left{std::move(rhs.left)},
|
||||||
right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr},
|
right{std::move(rhs.right)}, color{rhs.color}, prev{nullptr},
|
||||||
next{nullptr} {
|
next{nullptr}, map{rhs.map} {
|
||||||
this->left->parent = this;
|
if (this->left) {
|
||||||
this->right->parent = this;
|
this->left->parent = this;
|
||||||
|
}
|
||||||
|
if (this->right) {
|
||||||
|
this->right->parent = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Node &operator=(const Node &rhs) {
|
Node &operator=(const Node &rhs) {
|
||||||
// retain parent as is, common case is the copy or move is happening due
|
// retain parent as is, common case is the copy or move is happening due
|
||||||
|
@ -61,10 +70,16 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
this->left = std::make_unique<Node>(*rhs.left);
|
this->left = std::make_unique<Node>(*rhs.left);
|
||||||
this->right = std::make_unique<Node>(*rhs.right);
|
this->right = std::make_unique<Node>(*rhs.right);
|
||||||
|
|
||||||
this->left->parent = this;
|
if (this->left) {
|
||||||
this->right->parent = this;
|
this->left->parent = this;
|
||||||
this->left->restore_ordering();
|
this->left->restore_ordering();
|
||||||
this->right->restore_ordering();
|
}
|
||||||
|
if (this->right) {
|
||||||
|
this->right->parent = this;
|
||||||
|
this->right->restore_ordering();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->map = rhs.map;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
Node &operator=(Node &&rhs) {
|
Node &operator=(Node &&rhs) {
|
||||||
|
@ -74,10 +89,15 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
this->val = rhs.val;
|
this->val = rhs.val;
|
||||||
this->left = std::move(rhs.left);
|
this->left = std::move(rhs.left);
|
||||||
this->right = std::move(rhs.right);
|
this->right = std::move(rhs.right);
|
||||||
this->left->parent = this;
|
if (this->left) {
|
||||||
this->right->parent = this;
|
this->left->parent = this;
|
||||||
this->left->restore_ordering();
|
this->left->restore_ordering();
|
||||||
this->right->restore_ordering();
|
}
|
||||||
|
if (this->right) {
|
||||||
|
this->right->parent = this;
|
||||||
|
this->right->restore_ordering();
|
||||||
|
}
|
||||||
|
this->map = rhs.map;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
Node *child(Direction dir) {
|
Node *child(Direction dir) {
|
||||||
|
@ -150,8 +170,12 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
void restore_ordering() {
|
void restore_ordering() {
|
||||||
this->prev = this->calc_pred();
|
this->prev = this->calc_pred();
|
||||||
this->next = this->calc_succ();
|
this->next = this->calc_succ();
|
||||||
this->prev->next = this;
|
if (this->prev) {
|
||||||
this->next->prev = this;
|
this->prev->next = this;
|
||||||
|
}
|
||||||
|
if (this->next) {
|
||||||
|
this->next->prev = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Node *calc_pred() {
|
Node *calc_pred() {
|
||||||
if (this->left) {
|
if (this->left) {
|
||||||
|
@ -216,7 +240,67 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
this->parent->child(!dir)->set_child(dir, std::move(self));
|
this->parent->child(!dir)->set_child(dir, std::move(self));
|
||||||
}
|
}
|
||||||
// TODO:
|
// TODO:
|
||||||
void restore_red_black_insert() {}
|
// Referencing
|
||||||
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Notes_to_the_insert_diagrams
|
||||||
|
void restore_red_black_insert(Direction dir) {
|
||||||
|
Node *self = this;
|
||||||
|
|
||||||
|
// infinite loop for case 2's sake, if tail recursion optimization was
|
||||||
|
// guaranteed I'd use tail recursion
|
||||||
|
while (true) {
|
||||||
|
Node *parent = self->parent;
|
||||||
|
// we're root, no-op (case 3)
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is violated it's a bug
|
||||||
|
assert(parent->child(dir) == self);
|
||||||
|
|
||||||
|
// parent is black so no violation no-op (case 1)
|
||||||
|
if (parent->color == Color::Black) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *grandparent = parent->parent;
|
||||||
|
|
||||||
|
// parent is root (case 4)
|
||||||
|
if (!grandparent) {
|
||||||
|
parent->color = Color::Black;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// table showing transforms on wikipedia doesn't have this so if it
|
||||||
|
// happens it's probably a bug
|
||||||
|
assert(grandparent->color == Color::Black);
|
||||||
|
|
||||||
|
Node *uncle = grandparent->child(!grandparent->which_child(parent));
|
||||||
|
if (uncle == nullptr || uncle->color == Color::Black) {
|
||||||
|
if (parent->which_child(self) != grandparent->which_child(parent)) {
|
||||||
|
// we're an inner child
|
||||||
|
// case 5
|
||||||
|
parent->rotate(dir);
|
||||||
|
self = parent;
|
||||||
|
parent = self->parent;
|
||||||
|
}
|
||||||
|
// case 6
|
||||||
|
if (grandparent->parent == nullptr) {
|
||||||
|
map->rotate_root(!dir);
|
||||||
|
} else {
|
||||||
|
grandparent->rotate(!dir);
|
||||||
|
}
|
||||||
|
parent->color = Color::Black;
|
||||||
|
grandparent->color = Color::Red;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2 (by process of elimination)
|
||||||
|
parent->color = Color::Black;
|
||||||
|
uncle->color = Color::Black;
|
||||||
|
grandparent->color = Color::Red;
|
||||||
|
self = grandparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO:
|
// TODO:
|
||||||
void restore_red_black_erase() {}
|
void restore_red_black_erase() {}
|
||||||
};
|
};
|
||||||
|
@ -228,6 +312,7 @@ template <typename Key_T, typename Mapped_T> class Map {
|
||||||
Node *max;
|
Node *max;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
friend Node;
|
||||||
// public type definitions
|
// public type definitions
|
||||||
class Iterator {
|
class Iterator {
|
||||||
Node *underlying;
|
Node *underlying;
|
||||||
|
@ -255,6 +340,21 @@ public:
|
||||||
std::size_t size() { return this->_size; }
|
std::size_t size() { return this->_size; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// private helpers
|
||||||
|
void rotate_root(Direction dir) {
|
||||||
|
assert(root.has_value());
|
||||||
|
|
||||||
|
std::unique_ptr<Node> new_root = root.value().uchild(!dir);
|
||||||
|
// can't make null the new root
|
||||||
|
assert(new_root);
|
||||||
|
|
||||||
|
std::unique_ptr<Node> old_root =
|
||||||
|
std::make_unique<Node>(std::move(root.value()));
|
||||||
|
|
||||||
|
root.value() = std::move(*new_root);
|
||||||
|
|
||||||
|
root.value().set_child(dir, std::move(old_root));
|
||||||
|
}
|
||||||
template <bool trace = false>
|
template <bool trace = false>
|
||||||
std::pair<Node *, Direction> locate(const Key_T &key) {
|
std::pair<Node *, Direction> locate(const Key_T &key) {
|
||||||
Node *ret_parent;
|
Node *ret_parent;
|
||||||
|
@ -349,21 +449,21 @@ public:
|
||||||
if (this->root.has_value()) {
|
if (this->root.has_value()) {
|
||||||
return std::make_pair(Iterator{&root.value()}, false);
|
return std::make_pair(Iterator{&root.value()}, false);
|
||||||
} else {
|
} else {
|
||||||
this->root = Node{val};
|
this->root = Node{val, this};
|
||||||
return std::make_pair(Iterator{&root.value()}, true);
|
return std::make_pair(Iterator{&root.value()}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-root node
|
// non-root node
|
||||||
if (!parent->child(dir)) {
|
if (parent->child(dir)) {
|
||||||
// node already present
|
// node already present
|
||||||
return std::make_pair(Iterator{parent->child(dir)}, false);
|
return std::make_pair(Iterator{parent->child(dir)}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to insert non-root node
|
// need to insert non-root node
|
||||||
Node *new_node =
|
Node *new_node =
|
||||||
parent->set_child(dir, std::make_unique<Node>(Node{val})).get();
|
parent->set_child(dir, std::make_unique<Node>(Node{val, this})).get();
|
||||||
new_node->restore_red_black_insert();
|
new_node->restore_red_black_insert(dir);
|
||||||
new_node->restore_ordering();
|
new_node->restore_ordering();
|
||||||
return std::make_pair(Iterator{new_node}, true);
|
return std::make_pair(Iterator{new_node}, true);
|
||||||
}
|
}
|
||||||
|
|
768
Map.hpp.old
768
Map.hpp.old
|
@ -1,768 +0,0 @@
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// everything is super interconnected so some forward declarations are needed at
|
|
||||||
// various points
|
|
||||||
namespace cs440 {
|
|
||||||
|
|
||||||
template <typename Key_T, typename Mapped_T> class Map;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
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 {
|
|
||||||
using Self = BookKeeping<Key_T, Mapped_T>;
|
|
||||||
using ValueType = std::pair<Key_T, Mapped_T>;
|
|
||||||
using Ptr = typename std::vector<Self>::iterator;
|
|
||||||
friend class Map<Key_T, Mapped_T>;
|
|
||||||
Map<Key_T, Mapped_T> &container;
|
|
||||||
ValueType value;
|
|
||||||
// Ptr self;
|
|
||||||
Color color;
|
|
||||||
// nullptr indicates empty
|
|
||||||
std::optional<std::size_t> parent;
|
|
||||||
std::optional<std::size_t> left;
|
|
||||||
std::optional<std::size_t> right;
|
|
||||||
std::optional<std::size_t> prev;
|
|
||||||
std::optional<std::size_t> next;
|
|
||||||
BookKeeping(Map<Key_T, Mapped_T> &container) : container{container} {}
|
|
||||||
BookKeeping(BookKeeping const &rhs)
|
|
||||||
: container{rhs.container}, value{rhs.value}, // self{rhs.self},
|
|
||||||
color{rhs.color}, parent{rhs.parent}, left{rhs.left}, right{rhs.right},
|
|
||||||
prev{rhs.prev}, next{rhs.next} {}
|
|
||||||
// if pointing to different containers throws
|
|
||||||
BookKeeping &operator=(BookKeeping const &rhs) {
|
|
||||||
if (&this->container != &rhs.container) {
|
|
||||||
throw std::invalid_argument{"can only reassign Bookkeeping "
|
|
||||||
"values/iterators from the same map object"};
|
|
||||||
}
|
|
||||||
this->value = rhs.value;
|
|
||||||
// this->self = rhs.self;
|
|
||||||
this->color = rhs.color;
|
|
||||||
this->parent = rhs.parent;
|
|
||||||
this->left = rhs.left;
|
|
||||||
this->right = rhs.right;
|
|
||||||
this->prev = rhs.prev;
|
|
||||||
this->next = rhs.next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
// reference to a pointer because the alternatives were worse
|
|
||||||
inline Self *child(Direction dir) {
|
|
||||||
auto ret = c_select(dir);
|
|
||||||
return ret.has_value() ? &container.nodes[ret.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline std::optional<std::size_t> c_select(Direction dir) {
|
|
||||||
switch (dir) {
|
|
||||||
case Direction::Left:
|
|
||||||
return left;
|
|
||||||
break;
|
|
||||||
case Direction::Right:
|
|
||||||
return right;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inline void c_trans(Direction dir, Self *v) {
|
|
||||||
switch (dir) {
|
|
||||||
case Direction::Left:
|
|
||||||
this->set_l(v);
|
|
||||||
break;
|
|
||||||
case Direction::Right:
|
|
||||||
this->set_r(v);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Self *n() {
|
|
||||||
return next.has_value() ? &container.nodes[next.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline void set_n(Self *ptr) {
|
|
||||||
this->next = ptr == nullptr
|
|
||||||
? std::nullopt
|
|
||||||
: std::optional<std::size_t>{static_cast<std::size_t>(
|
|
||||||
ptr - &this->container.nodes[0])};
|
|
||||||
}
|
|
||||||
inline Self *p() {
|
|
||||||
return prev.has_value() ? &container.nodes[prev.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline void set_p(Self *ptr) {
|
|
||||||
this->prev = ptr == nullptr
|
|
||||||
? std::nullopt
|
|
||||||
: std::optional<std::size_t>{static_cast<std::size_t>(
|
|
||||||
ptr - &this->container.nodes[0])};
|
|
||||||
}
|
|
||||||
inline Self *r() {
|
|
||||||
return right.has_value() ? &container.nodes[right.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline void set_r(Self *ptr) {
|
|
||||||
this->right = ptr == nullptr
|
|
||||||
? std::nullopt
|
|
||||||
: std::optional<std::size_t>{static_cast<std::size_t>(
|
|
||||||
ptr - &this->container.nodes[0])};
|
|
||||||
}
|
|
||||||
inline Self *l() {
|
|
||||||
return left.has_value() ? &container.nodes[left.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline void set_l(Self *ptr) {
|
|
||||||
this->left = ptr == nullptr
|
|
||||||
? std::nullopt
|
|
||||||
: std::optional<std::size_t>{static_cast<std::size_t>(
|
|
||||||
ptr - &this->container.nodes[0])};
|
|
||||||
}
|
|
||||||
inline Self *par() {
|
|
||||||
return parent.has_value() ? &container.nodes[parent.value()] : nullptr;
|
|
||||||
}
|
|
||||||
inline void set_par(Self *ptr) {
|
|
||||||
this->parent = ptr == nullptr
|
|
||||||
? std::nullopt
|
|
||||||
: std::optional<std::size_t>{static_cast<std::size_t>(
|
|
||||||
ptr - &this->container.nodes[0])};
|
|
||||||
}
|
|
||||||
// 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->par();
|
|
||||||
Self *S = P->child(!dir);
|
|
||||||
Self *C;
|
|
||||||
|
|
||||||
// this tidbit is wrong and wikipedia is wrong to have this assert it seems
|
|
||||||
// this method shouldn't be called in cases where this assert will trip
|
|
||||||
// assert(S != nullptr);
|
|
||||||
|
|
||||||
C = S->child(dir);
|
|
||||||
P->c_trans(!dir, C);
|
|
||||||
|
|
||||||
if (C != nullptr) {
|
|
||||||
C->set_par(P);
|
|
||||||
}
|
|
||||||
S->c_trans(dir, P);
|
|
||||||
P->set_par(S);
|
|
||||||
S->set_par(G);
|
|
||||||
if (G != nullptr) {
|
|
||||||
if (P == G->r()) {
|
|
||||||
G->set_r(S);
|
|
||||||
} else {
|
|
||||||
G->set_l(S);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
T.root = S - &T.nodes[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
|
||||||
template <typename Key_T, typename Mapped_T> class Map {
|
|
||||||
private:
|
|
||||||
using ValueType = std::pair<Key_T, Mapped_T>;
|
|
||||||
using Node = BookKeeping<Key_T, Mapped_T>;
|
|
||||||
using Map_T = Map<Key_T, Mapped_T>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Iterator;
|
|
||||||
class ConstIterator;
|
|
||||||
class ReverseIterator;
|
|
||||||
|
|
||||||
friend class Iterator;
|
|
||||||
friend class ConstIterator;
|
|
||||||
friend class ReverseIterator;
|
|
||||||
friend Node;
|
|
||||||
class Iterator {
|
|
||||||
friend Map_T;
|
|
||||||
friend Node;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using Ref_T = std::optional<std::size_t>;
|
|
||||||
Map<Key_T, Mapped_T> &parent;
|
|
||||||
Ref_T ref;
|
|
||||||
Ref_T escape;
|
|
||||||
Iterator(Map<Key_T, Mapped_T> &parent, Ref_T ref,
|
|
||||||
Ref_T escape = std::nullopt)
|
|
||||||
: parent{parent}, ref{ref}, escape{escape} {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Iterator() = delete;
|
|
||||||
Iterator &operator++() {
|
|
||||||
if (ref == std::nullopt) {
|
|
||||||
ref = escape;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
if (parent.nodes[ref.value()].next == std::nullopt) {
|
|
||||||
escape = ref;
|
|
||||||
}
|
|
||||||
ref = parent.nodes[ref.value()].next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Iterator operator++(int) {
|
|
||||||
Iterator tmp = *this;
|
|
||||||
++(*this);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
Iterator &operator--() {
|
|
||||||
if (ref == std::nullopt) {
|
|
||||||
ref = escape;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
if (parent.nodes[ref.value()].prev == std::nullopt) {
|
|
||||||
escape = ref;
|
|
||||||
}
|
|
||||||
ref = parent.nodes[ref.value()].prev;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Iterator operator--(int) {
|
|
||||||
Iterator tmp = *this;
|
|
||||||
--(*this);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
ValueType &operator*() const {
|
|
||||||
return this->parent.nodes[this->ref.value()].value;
|
|
||||||
}
|
|
||||||
ValueType *operator->() const { return &this->operator*(); }
|
|
||||||
friend bool operator==(Iterator const &lhs, Iterator const &rhs) {
|
|
||||||
return lhs.ref == rhs.ref;
|
|
||||||
}
|
|
||||||
friend bool operator!=(Iterator const &lhs, Iterator const &rhs) {
|
|
||||||
return lhs.ref != rhs.ref;
|
|
||||||
}
|
|
||||||
friend bool operator==(ConstIterator const &lhs, Iterator const &rhs) {
|
|
||||||
return lhs.store_iter.ref == rhs.ref;
|
|
||||||
}
|
|
||||||
friend bool operator!=(ConstIterator const &lhs, Iterator const &rhs) {
|
|
||||||
return lhs.store_iter.ref != rhs.ref;
|
|
||||||
}
|
|
||||||
friend bool operator==(Iterator const &lhs, ConstIterator const &rhs) {
|
|
||||||
return lhs.ref == rhs.store_iter.ref;
|
|
||||||
}
|
|
||||||
friend bool operator!=(Iterator const &lhs, ConstIterator const &rhs) {
|
|
||||||
return lhs.ref != rhs.store_iter.ref;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class ConstIterator {
|
|
||||||
public:
|
|
||||||
friend class Map<Key_T, Mapped_T>;
|
|
||||||
friend class Iterator;
|
|
||||||
using underlying = Iterator;
|
|
||||||
|
|
||||||
private:
|
|
||||||
underlying store_iter;
|
|
||||||
ConstIterator(underlying iter) : store_iter{iter} {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConstIterator() = delete;
|
|
||||||
friend bool operator==(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
||||||
return lhs.store_iter == rhs.store_iter;
|
|
||||||
}
|
|
||||||
ConstIterator &operator++() {
|
|
||||||
++this->store_iter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ConstIterator operator++(int) {
|
|
||||||
ConstIterator tmp = *this;
|
|
||||||
this->store_iter++;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
ConstIterator &operator--() {
|
|
||||||
--this->store_iter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ConstIterator operator--(int) {
|
|
||||||
ConstIterator tmp = *this;
|
|
||||||
this->store_iter--;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
const ValueType &operator*() const { return *this->store_iter; }
|
|
||||||
const ValueType *operator->() const {
|
|
||||||
return this->store_iter.operator->();
|
|
||||||
}
|
|
||||||
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
||||||
return lhs.store_iter != rhs.store_iter;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class ReverseIterator {
|
|
||||||
public:
|
|
||||||
friend class Map<Key_T, Mapped_T>;
|
|
||||||
friend class Iterator;
|
|
||||||
using underlying = Iterator;
|
|
||||||
|
|
||||||
private:
|
|
||||||
underlying store_iter;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ReverseIterator() = delete;
|
|
||||||
ReverseIterator(underlying store_iter) : store_iter{store_iter} {}
|
|
||||||
ReverseIterator &operator++() {
|
|
||||||
--store_iter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ReverseIterator operator++(int) {
|
|
||||||
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,
|
|
||||||
ReverseIterator const &rhs) {
|
|
||||||
return lhs.store_iter == rhs.store_iter;
|
|
||||||
}
|
|
||||||
friend bool operator!=(ConstIterator const &lhs, ConstIterator const &rhs) {
|
|
||||||
return lhs.store_iter != rhs.store_iter;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<std::size_t> root;
|
|
||||||
std::optional<std::size_t> min;
|
|
||||||
std::optional<std::size_t> max;
|
|
||||||
std::vector<Node> nodes;
|
|
||||||
std::size_t size_diff;
|
|
||||||
|
|
||||||
std::optional<std::size_t> pred(std::size_t node) {
|
|
||||||
if (this->nodes[node].left.has_value()) {
|
|
||||||
std::size_t store = this->nodes[node].left.value();
|
|
||||||
while (this->nodes[store].right.has_value()) {
|
|
||||||
store = this->nodes[node].right.value();
|
|
||||||
}
|
|
||||||
return store;
|
|
||||||
} else {
|
|
||||||
if (!this->nodes[node].parent.has_value()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t prev_store = node;
|
|
||||||
std::size_t store = this->nodes[node].parent.value();
|
|
||||||
while (this->nodes[store].parent.has_value()) {
|
|
||||||
if (this->nodes[store].right == prev_store) {
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
prev_store = store;
|
|
||||||
store = this->nodes[store].parent.value();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<std::size_t> succ(std::size_t node) {
|
|
||||||
if (this->nodes[node].right.has_value()) {
|
|
||||||
std::size_t store = this->nodes[node].right.value();
|
|
||||||
while (this->nodes[store].left.has_value()) {
|
|
||||||
store = this->nodes[node].left.value();
|
|
||||||
}
|
|
||||||
return store;
|
|
||||||
} else {
|
|
||||||
if (!this->nodes[node].parent.has_value()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t prev_store = node;
|
|
||||||
std::size_t store = this->nodes[prev_store].parent.value();
|
|
||||||
while (this->nodes[store].parent.has_value()) {
|
|
||||||
if (this->nodes[store].left == prev_store) {
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
prev_store = store;
|
|
||||||
store = this->nodes[store].parent.value();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Map()
|
|
||||||
: root{std::nullopt}, min{std::nullopt}, max{std::nullopt}, nodes{},
|
|
||||||
size_diff{0} {}
|
|
||||||
Map(const Map &rhs)
|
|
||||||
: root{rhs.root}, min{rhs.min}, max{rhs.max}, nodes{rhs.nodes},
|
|
||||||
size_diff{0} {}
|
|
||||||
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{std::nullopt}, min{std::nullopt}, max{std::nullopt}, nodes{} {
|
|
||||||
this->insert(elems.begin(), elems.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
~Map() {}
|
|
||||||
|
|
||||||
size_t size() const { return this->nodes.size() - this->size_diff; }
|
|
||||||
bool empty() const { return this->size() == 0; }
|
|
||||||
|
|
||||||
Iterator begin() { return Iterator{*this, min}; }
|
|
||||||
Iterator end() { return Iterator{*this, std::nullopt, max}; }
|
|
||||||
ConstIterator begin() const { return ConstIterator{*this, this->begin()}; }
|
|
||||||
ConstIterator end() const { return ConstIterator{*this, this->end()}; }
|
|
||||||
ConstIterator cbegin() const { return this->begin(); }
|
|
||||||
ConstIterator cend() const { return this->end(); }
|
|
||||||
ReverseIterator rbegin() {
|
|
||||||
return ReverseIterator{Iterator{*this, this->max}};
|
|
||||||
}
|
|
||||||
ReverseIterator rend() {
|
|
||||||
return ReverseIterator{Iterator{*this, nullptr, min}};
|
|
||||||
}
|
|
||||||
Iterator find(const Key_T &key) {
|
|
||||||
// we need a locate slot function for insert regardless so might as well use
|
|
||||||
// it here
|
|
||||||
auto [parent, dir] = this->locate_slot(key);
|
|
||||||
if (!parent.has_value()) {
|
|
||||||
if (this->root.has_value() &&
|
|
||||||
this->nodes[this->root.value()].value.first == key) {
|
|
||||||
return Iterator{*this, root};
|
|
||||||
} else {
|
|
||||||
return this->end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this->nodes[parent.value()].c_select(dir).has_value()) {
|
|
||||||
return this->end();
|
|
||||||
}
|
|
||||||
return Iterator{*this, this->nodes[parent.value()].c_select(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) {
|
|
||||||
auto ret = this->find(key);
|
|
||||||
if (ret == this->end()) {
|
|
||||||
throw std::out_of_range{"Key not in map"};
|
|
||||||
}
|
|
||||||
return ret->second;
|
|
||||||
}
|
|
||||||
const Mapped_T &at(const Key_T &key) const {
|
|
||||||
auto ret = this->find(key);
|
|
||||||
if (ret == this->end()) {
|
|
||||||
throw std::out_of_range{"Key not in map"};
|
|
||||||
}
|
|
||||||
return ret->second;
|
|
||||||
}
|
|
||||||
Mapped_T &operator[](const Key_T &key) {
|
|
||||||
Mapped_T v;
|
|
||||||
auto insert_val = std::make_pair(key, v);
|
|
||||||
auto [iter, key_no_exist] = this->insert(insert_val);
|
|
||||||
return iter->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void handle_root_rotation(std::optional<std::size_t> g,
|
|
||||||
std::optional<std::size_t> p,
|
|
||||||
std::optional<std::size_t> i, Direction dir) {
|
|
||||||
handle_root_rotation(g.has_value() ? &this->nodes[g.value()] : nullptr,
|
|
||||||
p.has_value() ? &this->nodes[p.value()] : nullptr,
|
|
||||||
i.has_value() ? &this->nodes[i.value()] : nullptr,
|
|
||||||
dir);
|
|
||||||
}
|
|
||||||
void handle_root_rotation(Node *grandparent, Node *parent, Node *inserting,
|
|
||||||
Direction dir) {
|
|
||||||
// making inner grandchild into outer grandchild
|
|
||||||
|
|
||||||
if (inserting == parent->child(!dir)) {
|
|
||||||
parent->rotate(dir);
|
|
||||||
inserting = parent;
|
|
||||||
parent = grandparent->child(dir);
|
|
||||||
}
|
|
||||||
grandparent->rotate(!dir);
|
|
||||||
|
|
||||||
parent->color = Color::Black;
|
|
||||||
grandparent->color = Color::Red;
|
|
||||||
}
|
|
||||||
using Ref_T = std::optional<std::size_t>;
|
|
||||||
// heavily referencing the wikipedia implementation for this
|
|
||||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
|
|
||||||
void insert_helper(Ref_T to_insert, Ref_T parent, Direction dir) {
|
|
||||||
// initialize the element we're inserting
|
|
||||||
this->nodes[to_insert.value()].color = Color::Red;
|
|
||||||
this->nodes[to_insert.value()].left = std::nullopt;
|
|
||||||
this->nodes[to_insert.value()].right = std::nullopt;
|
|
||||||
this->nodes[to_insert.value()].next = std::nullopt;
|
|
||||||
this->nodes[to_insert.value()].prev = std::nullopt;
|
|
||||||
this->nodes[to_insert.value()].parent = parent;
|
|
||||||
|
|
||||||
// if this is the first element to be inserted it's root
|
|
||||||
if (!this->nodes[to_insert.value()].parent.has_value()) {
|
|
||||||
this->root = to_insert;
|
|
||||||
this->nodes[to_insert.value()].color = Color::Black;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (dir) {
|
|
||||||
case Direction::Left:
|
|
||||||
this->nodes[parent.value()].left = to_insert;
|
|
||||||
break;
|
|
||||||
case Direction::Right:
|
|
||||||
this->nodes[parent.value()].right = to_insert;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
// don't need to keep track of these in between loops they get
|
|
||||||
// recalculated
|
|
||||||
std::optional<std::size_t> grandparent;
|
|
||||||
std::optional<std::size_t> uncle;
|
|
||||||
if (this->nodes[parent.value()].color == Color::Black) {
|
|
||||||
// black parent means invariants definitely hold
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
grandparent = this->nodes[parent.value()].parent;
|
|
||||||
|
|
||||||
if (!grandparent.has_value()) {
|
|
||||||
// parent is root, just need to recolor it to black
|
|
||||||
this->nodes[parent.value()].color = Color::Black;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Direction parent_direction;
|
|
||||||
if (this->nodes[grandparent.value()].left == parent) {
|
|
||||||
parent_direction = Direction::Left;
|
|
||||||
uncle = this->nodes[grandparent.value()].right;
|
|
||||||
} else {
|
|
||||||
parent_direction = Direction::Right;
|
|
||||||
uncle = this->nodes[grandparent.value()].left;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uncle.has_value() ||
|
|
||||||
this->nodes[uncle.value()].color == Color::Black) {
|
|
||||||
if (to_insert == this->nodes[parent.value()].c_select(!dir)) {
|
|
||||||
// case 5
|
|
||||||
this->nodes[parent.value()].rotate(dir);
|
|
||||||
to_insert = parent;
|
|
||||||
parent = this->nodes[grandparent.value()].c_select(dir);
|
|
||||||
}
|
|
||||||
// case 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
|
|
||||||
this->nodes[parent.value()].color = Color::Black;
|
|
||||||
this->nodes[uncle.value()].color = Color::Black;
|
|
||||||
this->nodes[grandparent.value()].color = Color::Red;
|
|
||||||
|
|
||||||
to_insert = grandparent;
|
|
||||||
parent = this->nodes[to_insert.value()].parent;
|
|
||||||
} while (parent.has_value());
|
|
||||||
|
|
||||||
// case 3: current node is red root so we're done
|
|
||||||
}
|
|
||||||
// returns nullptr iff map is empty
|
|
||||||
std::pair<std::optional<std::size_t>, Direction>
|
|
||||||
locate_slot(const Key_T &key) {
|
|
||||||
using Ref_T = std::optional<std::size_t>;
|
|
||||||
Ref_T current = this->root;
|
|
||||||
Ref_T parent = std::nullopt;
|
|
||||||
Direction dir;
|
|
||||||
while (current.has_value() &&
|
|
||||||
this->nodes[current.value()].value.first != key) {
|
|
||||||
parent = current;
|
|
||||||
if (key < this->nodes[current.value()].value.first) {
|
|
||||||
dir = Direction::Left;
|
|
||||||
current = this->nodes[current.value()].left;
|
|
||||||
} else {
|
|
||||||
dir = Direction::Right;
|
|
||||||
current = this->nodes[current.value()].right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_pair(parent, dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// If the key does not already exist in the map, it returns an iterator
|
|
||||||
// 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) {
|
|
||||||
auto [parent, dir] = locate_slot(val.first);
|
|
||||||
bool ret = !parent.has_value() ||
|
|
||||||
!this->nodes[parent.value()].c_select(dir).has_value();
|
|
||||||
if (!ret) {
|
|
||||||
return std::make_pair(
|
|
||||||
Iterator{*this, this->nodes[parent.value()].c_select(dir)}, ret);
|
|
||||||
}
|
|
||||||
Node to_insert{*this};
|
|
||||||
to_insert.value = val;
|
|
||||||
this->nodes.push_back(std::move(to_insert));
|
|
||||||
// this->nodes.back().self = (--this->nodes.end());
|
|
||||||
insert_helper(nodes.size() - 1, parent, dir);
|
|
||||||
|
|
||||||
if (min == std::nullopt ||
|
|
||||||
val.first < this->nodes[min.value()].value.first) {
|
|
||||||
min = nodes.size() - 1;
|
|
||||||
}
|
|
||||||
if (max == std::nullopt ||
|
|
||||||
val.first > this->nodes[max.value()].value.first) {
|
|
||||||
max = nodes.size() - 1;
|
|
||||||
}
|
|
||||||
Ref_T successor = this->succ(this->nodes.size() - 1);
|
|
||||||
Ref_T predessor = this->pred(this->nodes.size() - 1);
|
|
||||||
if (successor.has_value()) {
|
|
||||||
this->nodes[successor.value()].prev = this->nodes.size() - 1;
|
|
||||||
}
|
|
||||||
this->nodes[this->nodes.size() - 1].next = successor;
|
|
||||||
if (predessor.has_value()) {
|
|
||||||
this->nodes[predessor.value()].next = this->nodes.size() - 1;
|
|
||||||
}
|
|
||||||
this->nodes[this->nodes.size() - 1].prev = successor;
|
|
||||||
return std::make_pair(Iterator(*this, nodes.size() - 1), ret);
|
|
||||||
}
|
|
||||||
template <typename IT_T> void insert(IT_T range_beg, IT_T range_end) {
|
|
||||||
std::for_each(range_beg, range_end,
|
|
||||||
[&](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;
|
|
||||||
}
|
|
||||||
// black leaf node with no kids
|
|
||||||
void complex_erase(Iterator pos) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void erase(Iterator pos) {
|
|
||||||
this->size_diff++;
|
|
||||||
// simple cases
|
|
||||||
Node *ref = &this->nodes[pos.ref.value()];
|
|
||||||
if (ref ==
|
|
||||||
(this->min.has_value() ? &this->nodes[this->min.value()] : nullptr)) {
|
|
||||||
this->min = ref->next;
|
|
||||||
}
|
|
||||||
if (ref ==
|
|
||||||
(this->max.has_value() ? &this->nodes[this->max.value()] : nullptr)) {
|
|
||||||
this->max = ref->prev;
|
|
||||||
}
|
|
||||||
// 2 children
|
|
||||||
if (ref->l() != nullptr && ref->r() != nullptr) {
|
|
||||||
Ref_T next = ref->next;
|
|
||||||
*ref = this->nodes[next.value()];
|
|
||||||
this->erase(Iterator{*this, next});
|
|
||||||
}
|
|
||||||
// single child which is left
|
|
||||||
else if (ref->l() != nullptr && ref->r() == nullptr) {
|
|
||||||
if (ref->par()->l() == ref) {
|
|
||||||
ref->par()->set_l(ref->l());
|
|
||||||
} else {
|
|
||||||
ref->par()->set_r(ref->l());
|
|
||||||
}
|
|
||||||
if (ref->color == Color::Black) {
|
|
||||||
ref->l()->color = Color::Black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// single child which is right
|
|
||||||
else if (ref->l() == nullptr && ref->r() != nullptr) {
|
|
||||||
if (ref->par()->l() == ref) {
|
|
||||||
ref->par()->set_l(ref->r());
|
|
||||||
} else {
|
|
||||||
ref->par()->set_r(ref->r());
|
|
||||||
}
|
|
||||||
if (ref->color == Color::Black) {
|
|
||||||
ref->r()->color = Color::Black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no children and root
|
|
||||||
else if (this->root == pos.ref && ref->l() == nullptr &&
|
|
||||||
ref->r() == nullptr) {
|
|
||||||
this->root = std::nullopt;
|
|
||||||
}
|
|
||||||
// no children and red
|
|
||||||
else if (ref->color == Color::Red && ref->l() == nullptr &&
|
|
||||||
ref->r() == nullptr) {
|
|
||||||
if (ref->parent.has_value()) {
|
|
||||||
if (this->nodes[ref->parent.value()].right == pos.ref.value()) {
|
|
||||||
this->nodes[ref->parent.value()].right = std::nullopt;
|
|
||||||
} else {
|
|
||||||
this->nodes[ref->parent.value()].left = std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// complicated case of black node with no kids
|
|
||||||
else {
|
|
||||||
this->complex_erase(pos);
|
|
||||||
}
|
|
||||||
if (ref->next.has_value()) {
|
|
||||||
this->nodes[ref->next.value()].prev = this->pred(ref->next.value());
|
|
||||||
}
|
|
||||||
if (ref->prev.has_value()) {
|
|
||||||
this->nodes[ref->next.value()].next = this->succ(ref->next.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void erase(const Key_T &key) { this->erase(this->find(key)); }
|
|
||||||
void clear() {
|
|
||||||
this->root = std::nullopt;
|
|
||||||
this->nodes.clear();
|
|
||||||
}
|
|
||||||
friend bool operator==(const Map &lhs, const Map &rhs) {
|
|
||||||
if (lhs.nodes.size() != rhs.nodes.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto liter = lhs.cbegin();
|
|
||||||
auto riter = rhs.cbegin();
|
|
||||||
// both must be the same length so this is fine
|
|
||||||
while (liter != lhs.cend()) {
|
|
||||||
if (*liter != *riter) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
liter++;
|
|
||||||
riter++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
friend bool operator!=(const Map &lhs, const Map &rhs) {
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
friend bool operator<(const Map &lhs, const Map &rhs) {
|
|
||||||
auto l_iter = lhs.cbegin();
|
|
||||||
auto r_iter = rhs.cbegin();
|
|
||||||
for (; l_iter != lhs.cend() && r_iter != rhs.cend(); l_iter++, r_iter++) {
|
|
||||||
if (*l_iter < *r_iter) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lhs.size() < rhs.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace cs440
|
|
Loading…
Reference in a new issue