-
Notifications
You must be signed in to change notification settings - Fork 0
/
RBT.cpp
368 lines (334 loc) · 12.3 KB
/
RBT.cpp
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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#include <iostream>
#include "RBT.h"
#include <queue>
using namespace std;
RBT::RBT() : root(nullptr) {}
void RBT::insertValue(ElementType val) {
Node *node = new Node(val);
root = insertAux(root, node);
fixInsert(node);
}
void RBT::deleteValue(ElementType val) {
Node *node = deleteAux(root, val);
fixDelete(node);
}
bool RBT::search(ElementType val) {
return searchAux(val, root);
}
bool RBT::empty() {
return root == nullptr;
}
void RBT::inorder(vector<ElementType> &v) {
inorderAux(root, v);
}
void RBT::levelOrderPrint() {
if (empty()) {
cout << "empty tree";
return;
}
queue<Node *> q;
Node *cur = root;
q.push(root);
while (!q.empty()) {
int size = (int) q.size();
while (size--) {
cur = q.front();
q.pop();
cout << cur->data << ' ';
if (cur->left != nullptr)
q.push(cur->left);
if (cur->right != nullptr)
q.push(cur->right);
}
cout << '\n';
}
cout << '\n';
}
void RBT::clear() {
if (root == nullptr)
return;
clearAux(root);
}
RBT::~RBT() {
clear();
}
int RBT::getColor(RBT::Node *&node) {
if (node == nullptr)
return BLACK;
return node->color;
}
void RBT::setColor(RBT::Node *&node, int color) {
if (node == nullptr)
return;
node->color = color;
}
RBT::Node *RBT::insertAux(RBT::Node *&root, RBT::Node *&node) {
if (root == nullptr)
return node;
if (node->data < root->data) {
root->left = insertAux(root->left, node);
root->left->parent = root;
} else if (node->data > root->data) {
root->right = insertAux(root->right, node);
root->right->parent = root;
} else {
cerr << "cannot have duplicates in the tree. exiting program";
}
return root;
}
void RBT::fixInsert(RBT::Node *&node) {
Node *parent = nullptr, *grandParent = nullptr;
//iteratively fix the tree from bottom to top
while (node != root && getColor(node) == RED && getColor(node->parent) == RED) {
parent = node->parent;
grandParent = parent->parent;
Node *uncle;
if (parent == grandParent->left) {
uncle = grandParent->right;
if (getColor(uncle) == RED) { //simple case, we just recolor
setColor(uncle, BLACK);
setColor(parent, BLACK);
setColor(grandParent, RED);
node = grandParent;
//now we are done with this iteration, in the next iteration we check if recoloring the grandparent
//cause any conflicts
} else { //black uncle case, this means that we must make rotations
if (node == parent->right) { //left right case
rotateLeft(parent); //turn it to a left left case
//fix the pointers after rotation
node = parent;
parent = node->parent;
}
rotateRight(grandParent); // fix the left left case
swap(grandParent->color, parent->color); //recoloring
node = parent; //now the parent is the new root of the subtree, so we will fix it in the next iteration
}
} else {
//this is the mirror of the above case
uncle = grandParent->left;
if (getColor(uncle) == RED) {
setColor(uncle, BLACK);
setColor(parent, BLACK);
setColor(grandParent, RED);
node = grandParent;
} else {
if (node == parent->left) {
rotateRight(parent);
node = parent;
parent = node->parent;
}
rotateLeft(grandParent);
swap(grandParent->color, parent->color);
node = parent;
}
}
}
setColor(root, BLACK); //root should always be black
}
RBT::Node *RBT::minValueNode(RBT::Node *&node) {
Node *temp = node;
while (temp->left != nullptr)
temp = temp->left;
return temp;
}
void RBT::rotateRight(RBT::Node *&node) {
Node *leftChild = node->left; //my left child who will later become my parent
node->left = leftChild->right;
//if my left now is not null we need to change its parent pointer to point to me
if (node->left != nullptr)
node->left->parent = node;
//now my old left child's parent should be my old parent
leftChild->parent = node->parent;
//if my parent was null this means I was the root and my left child is the new root now
if (node->parent == nullptr)
root = leftChild;
else if (node == node->parent->left) //if I was a left child then my left child should replace me as left
node->parent->left = leftChild;
else //else my left child will replace me as right
node->parent->right = leftChild;
leftChild->right = node; //I am now the right child of my old left child
node->parent = leftChild; // and my old left child is now my parent
}
void RBT::rotateLeft(RBT::Node *&node) {
Node *rightChild = node->right;
node->right = rightChild->left;
if (node->right != nullptr)
node->right->parent = node;
rightChild->parent = node->parent;
if (node->parent == nullptr)
root = rightChild;
else if (node == node->parent->right)
node->parent->right = rightChild;
else
node->parent->left = rightChild;
rightChild->left = node;
node->parent = rightChild;
}
RBT::Node *RBT::deleteAux(RBT::Node *&node, int val) {
if (node == nullptr)
return node;
if (node->data > val)
return deleteAux(node->left, val);
if (val > node->data)
return deleteAux(node->right, val);
//found
if (node->left == nullptr || node->right == nullptr)
return node; //no need to switch with successor, already a 1 child or 0 child case
Node *successor = minValueNode(node->right);
node->data = successor->data;
return deleteAux(node->right, successor->data);
}
void RBT::fixDelete(RBT::Node *&node) {
//Deletion cases:
//case 1: deleting a red node
//case 2: double black root
//case 3: double black's sibling is black and both its children are black
//case 4: double black's sibling is red
//case 5: double black's sibling is black and the far nephew is black but the near nephew is red
//case 6: double black's sibling is black and the far nephew is red
if (node == nullptr)
return;
if (node == root) {
if (node->right != nullptr)
root = node->right;
else if (node->left != nullptr)
root = node->left;
else
root = nullptr;
delete node;
setColor(root, BLACK);
return;
}
if (getColor(node) == RED || getColor(node->left) == RED || getColor(node->right) == RED) {
//case 1
//if the node was red just delete it
//if the node has 1 red child:
//replace the node with the red child and then recolor the child to black, and we are done here
Node *child = node->left != nullptr ? node->left : node->right;
if (node == node->parent->left) {
node->parent->left = child;
if (child != nullptr)
child->parent = node->parent;
setColor(child, BLACK);
delete (node);
} else {
node->parent->right = child;
if (child != nullptr)
child->parent = node->parent;
setColor(child, BLACK);
delete (node);
}
} else {
//complex cases
Node *sibling = nullptr;
Node *parent = nullptr;
Node *ptr = node;
setColor(ptr, DOUBLE_BLACK); //mark myself as double black
//iteratively fix the node from bottom to top
// if I was the root then this is case 2, and we do not need to do anything
while (ptr != root && getColor(ptr) == DOUBLE_BLACK) {
parent = ptr->parent;
if (ptr == parent->left) {
sibling = parent->right; //get my sibling
//check the sibling
if (getColor(sibling) == RED) {
//case 4
//swap the color of my sibling and my parent and rotate my parent to my direction
setColor(sibling, BLACK);
setColor(parent, RED);
rotateLeft(parent);
//because the double black was not removed,
//we are not done here, in the next iteration we will reapply the cases
} else {
//black sibling cases
if (getColor(sibling->left) == BLACK && getColor(sibling->right) == BLACK) {
//case 3
setColor(sibling, RED);
if (getColor(parent) == RED)
setColor(parent, BLACK);
else
setColor(parent, DOUBLE_BLACK);
ptr = parent;
//if the double black was passed to the parent, we will fix it in the next iteration
} else {
if (getColor(sibling->right) == BLACK) { //check my far nephew
//case 5
setColor(sibling->left, BLACK);
setColor(sibling, RED);
rotateRight(sibling);
sibling = parent->right; //readjust the sibling pointer after the rotation
}
//case 6
//since case 5 will always transpose into case 6, no need for an if condition here
setColor(sibling, parent->color);
setColor(parent, BLACK);
setColor(sibling->right, BLACK);
rotateLeft(parent);
break; //case 6 is a terminal case so no need to reiterate.
}
}
} else {
//this is the mirror of the above block
sibling = parent->left;
if (getColor(sibling) == RED) {
setColor(sibling, BLACK);
setColor(parent, RED);
rotateRight(parent);
} else {
if (getColor(sibling->left) == BLACK && getColor(sibling->right) == BLACK) {
setColor(sibling, RED);
if (getColor(parent) == RED)
setColor(parent, BLACK);
else
setColor(parent, DOUBLE_BLACK);
ptr = parent;
} else {
if (getColor(sibling->left) == BLACK) {
setColor(sibling->right, BLACK);
setColor(sibling, RED);
rotateLeft(sibling);
sibling = parent->left;
}
setColor(sibling, parent->color);
setColor(parent, BLACK);
setColor(sibling->left, BLACK);
rotateRight(parent);
break;
}
}
}
}
//now we fix the parent pointers
if (node == node->parent->left)
node->parent->left = nullptr;
else
node->parent->right = nullptr;
delete (node); //and delete the node
setColor(root, BLACK); //in case the root became double black
}
}
void RBT::inorderAux(RBT::Node *&node, vector<ElementType> &v) {
if (node == nullptr)
return;
inorderAux(node->left, v);
v.push_back(node->data);
inorderAux(node->right, v);
}
void RBT::clearAux(RBT::Node *&node) {
if (node->left != nullptr)
clearAux(node->left);
if (node->right != nullptr)
clearAux(node->right);
delete node;
node = nullptr;
}
bool RBT::searchAux(ElementType val, RBT::Node *node) {
if (node == nullptr)
return false;
if (val > node->data)
return searchAux(val, node->right);
else if (val < node->data)
return searchAux(val, node->left);
else
return true;
}