-
Notifications
You must be signed in to change notification settings - Fork 0
/
RBTree.hpp
340 lines (302 loc) · 8.87 KB
/
RBTree.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#ifndef RBTREE_HPP
#define RBTREE_HPP
inline direction flip(direction orig) {
if (orig == direction::LEFT) {
return direction::RIGHT;
}
return direction::LEFT;
}
template <class T>
std::shared_ptr<RBTree<T>> RBTree<T>::createTree(comp_func_t comp_func) {
auto tree = std::make_shared<RBTree<T>>(comp_func, ctor_protector_t());
tree->m_root = RBNode::createNode(tree);
return tree;
}
template <class T>
RBTree<T>::RBNode::RBNode(std::weak_ptr<RBTree<T>> tree)
: m_color(color::BLACK)
, m_tree(tree) {}
template <class T>
RBTree<T>::RBNode::~RBNode() {
// Delete all children by shared_ptr magic
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::createNode(std::weak_ptr<RBTree<T>> tree) {
auto node = std::make_shared<RBNode>(tree);
node->m_self = node;
return node;
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::createChild() {
auto node = createNode(m_tree);
node->m_p = m_self;
return node;
}
template <class T>
bool RBTree<T>::RBNode::isNil() const {
return !m_key;
}
template <class T>
void RBTree<T>::RBNode::insert(T key) {
if (!m_key) {
m_key = std::make_unique<T>(key);
m_l = createChild();
m_r = createChild();
redden();
return;
}
int comp_res = m_tree.lock()->m_comp_func(key, *m_key);
if (comp_res == 0) {
throw KeyAlreadyExists();
}
if (comp_res > 0) {
m_r->insert(key);
return;
}
m_l->insert(key);
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::kill() {
std::shared_ptr<RBNode> retval = succ();
auto killed_node = m_self.lock();
if (m_r->m_key && m_l->m_key) {
killed_node = retval; // retval is the successor
retval = m_self.lock();
m_key = std::move(killed_node->m_key);
}
auto replacing_node = killed_node->m_l->m_key ? killed_node->m_l : killed_node->m_r;
auto killed_node_parent = killed_node->m_p.lock();
if (!killed_node_parent) {
// killed node is root
m_tree.lock()->m_root = replacing_node;
}
else {
auto killed_node_dir = killed_node->getDirectionFromParent();
if (killed_node_dir == direction::LEFT) {
killed_node_parent->m_l = replacing_node;
}
else {
killed_node_parent->m_r = replacing_node;
}
}
replacing_node->m_p = killed_node_parent;
if (killed_node->m_color == color::BLACK) {
replacing_node->blacken();
}
return retval;
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::succ() const {
return scan(direction::RIGHT);
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::pred() const {
return scan(direction::LEFT);
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::scan(direction dir) const {
auto ptr = getChild(dir);
if (ptr && ptr->m_key) {
// There is a child in the scanned direction, therefore successor is in subtree
while(ptr->m_key) {
// progress as much as possible in opposite direction in subtree
ptr = ptr->getChild(flip(dir));
}
// Now we reached a leaf without a value, returning direct parent
return ptr->m_p.lock();
}
ptr = m_self.lock();
while (ptr->m_p.lock() && ptr->getDirectionFromParent() == dir) {
ptr = ptr->m_p.lock();
}
if (!ptr) {
return ptr;
}
// return either the direct parent, or if this is the last node - a nullptr
return ptr->m_p.lock();
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::minimum() const {
if (!m_l || !m_l->m_key) {
return m_self.lock();
}
return m_l->minimum();
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::RBNode::getChild(direction dir) const {
if (dir == direction::LEFT) {
return m_l;
}
return m_r;
}
template <class T>
direction RBTree<T>::RBNode::getDirectionFromParent() const {
auto p = m_p.lock();
if (!p) {
throw std::runtime_error("Root node is looking for parent!");
}
if (p->m_l == m_self.lock()) {
return direction::LEFT;
}
if (p->m_r == m_self.lock()) {
return direction::RIGHT;
}
throw std::runtime_error("Parent does not know this node!");
}
template <class T>
void RBTree<T>::RBNode::redden() {
// Implementing as recursion is easier in this case
// as we get free updates of `this`
auto p = m_p.lock();
if (!p) {
// root, update red height by one
m_color = color::BLACK;
return;
}
m_color = color::RED;
if (p->m_color == color::BLACK) {
// All ok, can finish recursion
return;
}
auto grandpa = p->m_p.lock();
auto parent_dir = p->getDirectionFromParent();
auto uncle = grandpa->getChild(flip(parent_dir));
// Case 1
if (uncle->m_color == color::RED) {
// Red uncle, color father and uncle black and pass reddening upwards
p->m_color = color::BLACK;
uncle->m_color = color::BLACK;
grandpa->redden();
return;
}
auto dir = getDirectionFromParent();
// Case 2
if (dir != parent_dir) {
// Rotate into case 3
p->rotate(parent_dir);
p->redden();
return;
}
// Case 3
p->m_color = color::BLACK;
grandpa->m_color = color::RED;
grandpa->rotate(flip(parent_dir));
}
template <class T>
void RBTree<T>::RBNode::blacken() {
// More natural to implement using recursion
auto p = m_p.lock();
if (!p) {
// Node is root, decrease black height by one
return;
}
if (m_color == color::RED) {
// Blackening a red node is easy
m_color = color::BLACK;
return;
}
auto dir = getDirectionFromParent();
auto brother = p->getChild(flip(dir));
// Case 1
if (brother->m_color == color::RED) {
brother->m_color = color::BLACK;
p->m_color = color::RED;
p->rotate(dir);
// Move to black brother case
blacken();
return;
}
// Black brother
// Case 2
if (brother->m_l->m_color == color::BLACK && brother->m_r->m_color == color::BLACK) {
// Both brother's kids are black
// color brother red, and move extra black upwards
brother->m_color = color::RED;
p->blacken();
return;
}
// At least one kid of brother is red
// Case 3
if (brother->getChild(flip(dir))->m_color == color::BLACK) {
// Color same sided cousin in black and brother in red
// then rotate brother into opposing side cousin (red -> case 4)
brother->getChild(dir)->m_color = color::BLACK;
brother->m_color = color::RED;
brother->rotate(flip(dir));
blacken();
return;
}
// The remaining case is red opposite sided cousin
// Case 4
// Switch colors between (black) brother and parent, rotating parent and
// adding a black node on both sides of new parent, rebalancing the tree
brother->m_color = p->m_color;
p->m_color = color::BLACK;
brother->getChild(flip(dir))->m_color = color::BLACK;
p->rotate(dir);
}
template <class T>
void RBTree<T>::RBNode::rotate(direction dir) {
auto new_parent = getChild(flip(dir));
if (!new_parent || !new_parent->m_key) {
throw std::runtime_error("Attempting to rotate a NIL into node!");
}
auto old_parent = m_p.lock();
auto self = m_self.lock();
direction my_dir = direction::LEFT;
if (old_parent) {
my_dir = getDirectionFromParent();
}
m_p = new_parent;
new_parent->m_p = old_parent;
if (dir == direction::LEFT) {
m_r = new_parent->m_l;
m_r->m_p = self;
new_parent->m_l = self;
}
else {
m_l = new_parent->m_r;
m_l->m_p = self;
new_parent->m_r = self;
}
if (old_parent) {
if (my_dir == direction::LEFT) {
old_parent->m_l = new_parent;
}
else {
old_parent->m_r = new_parent;
}
}
else {
m_tree.lock()->m_root = new_parent;
}
}
template <class T>
const T& RBTree<T>::RBNode::get() const {
return *m_key;
}
template <class T>
RBTree<T>::RBTree(comp_func_t comp_func, ctor_protector_t ctor_protector)
: m_comp_func(comp_func) {
// prevent warnings while not allow user to call Ctor
ctor_protector = ctor_protector;
}
template <class T>
RBTree<T>::~RBTree() {}
template <class T>
void RBTree<T>::insert(T key) {
m_root->insert(key);
}
template <class T>
void RBTree<T>::remove(T key) {
auto node = m_root->find(key);
if (node->isNil()) {
throw KeyNotFound();
}
node->kill();
}
template <class T>
std::shared_ptr<typename RBTree<T>::RBNode> RBTree<T>::minimum() {
return m_root->minimum();
}
#endif