-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBinarySearchTreeInsertRemove.cpp
280 lines (220 loc) · 7.13 KB
/
BinarySearchTreeInsertRemove.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
// Inserting/Removing unique elements from a binary search tree.
//
// No re-balancing has been implemented to keep height of tree height at O(log(n))
// so performance can vary between O(log(n) to O(n) for insert/remove operations.
//
// Complier: Visual Studio 2013 (v120)
#include <iostream>
#include <memory>
#include <list>
#include <cassert>
#include <utility>
#include <chrono>
using namespace std;
#ifdef _DEBUG
static int destructed_nodes; // Static member assigned zero by default;
#endif
template <typename T>
class BinarySearchTree {
public:
virtual ~BinarySearchTree() { clear(); }
bool empty() const { return !root.get(); }
void clear() { clear(&root); }
bool insert(const T& key) {
if (empty()) {
root = unique_ptr<Node>(new Node{ key, nullptr, nullptr });
return true;
}
Node* current = root.get();
Node* parent = nullptr;
// Traverse tree downwards until we find an empty node.
while (current) {
parent = current;
// Duplicate key - all elements should be unique
if (key == current->data)
return false;
// If key is less go left or if key is greater go right.
current = (key < current->data) ? current->left.get() : current->right.get();
}
assert(current == nullptr);
// Insert key according to < or > parent data
if (key < parent->data)
parent->left.reset(new Node{ key });
else
parent->right.reset(new Node{ key });
return true;
}
// Removes a node. Returns true on success.
bool remove(const T& key) {
Node* current = root.get();
Node* parent = nullptr;
// Traverse downwards until we find an empty or matching node
while (current && current->data != key) {
parent = current;
// If key is less go left or if key is greater go right.
current = (key < current->data) ? current->left.get() : current->right.get();
}
assert(current == nullptr || current->data == key);
// Was the found in the tree?
if (!current)
return false;
if (current->right) {
// If a right subtree exists then then this node will be
// replaced with the minimum node in the right node's subtree.
// traverse down the right subtree to find the right subtree's minimum.
Node* replacement = current->right.get();
auto replacement_parent = current;
while (replacement->left) {
replacement_parent = replacement;
replacement = replacement->left.get();
}
assert(replacement != nullptr);
// Attach the deleted node's left child to its replacement.
replacement->left.reset(current->left.release());
Node* replacement_old_right = replacement->right.release();
Node* replace_parent_replacement_link = nullptr;
// If the node being deleted isn't the replacement's parent
// then remove the deleted node's right and attach it to replacement.
if (current->right.get() != replacement) {
replacement->right.reset(current->right.release());
replace_parent_replacement_link = replacement_old_right;
}
// Attach the replacement's old right to it's parent left
if (replacement_parent->left.get() == replacement) {
replacement = replacement_parent->left.release();
replacement_parent->left.reset(replace_parent_replacement_link);
}
// Attach the replacement's old right to it's parent right
else {
replacement = replacement_parent->right.release();
replacement_parent->right.reset(replace_parent_replacement_link);
}
replace_parent_child_link(parent, current, replacement);
// If the node being removed is the root replace it and re-attach the
// replacement nodes right subtree.
if (root.get() == current) {
replacement->right.reset(replacement_old_right);
root.reset(replacement);
}
}
else {
// If there was was no right subtree then just replace the current
// with its left child.
// If the node being removed is the root replace it.
if (root.get() == current)
root.reset(current->left.release());
replace_parent_child_link(parent, current, current->left.release());
}
return true;
}
// Traverse downwards displaying in a breadth first search pattern
void display_BFS() const {
list<Node*> queue;
queue.push_front(root.get());
while (!empty() && !queue.empty()) {
auto current = queue.back();
queue.pop_back();
cout << current->data << " ";
if (current->left.get())
queue.push_front(current->left.get());
if (current->right.get())
queue.push_front(current->right.get());
}
cout << endl;
}
private:
// Nodes remain under ownership of unique ptr so they will
// exist as long as the unique pointer does.
struct Node {
T data;
unique_ptr<Node> left;
unique_ptr<Node> right;
#ifdef _DEBUG
~Node() { --destructed_nodes; }
#endif
};
unique_ptr<Node> root = nullptr;
// Recursively delete all nodes
void clear(unique_ptr<Node>* node) {
// If nullptr do nothing
if (!node->get())
return;
if ( node->get()->left.get())
clear(&((*node)->left));
if ( (*node)->right.get())
clear(&((*node)->right));
node->reset(nullptr);
}
// Replace the link between parent and child with new link.
void replace_parent_child_link(Node* parent, Node* child, Node* new_link) {
// If the child is the root node do nothing.
if (!parent)
return;
// If the left child replace left link.
if (parent->left.get() == child)
parent->left.reset(new_link);
else
parent->right.reset(new_link);
}
};
#ifdef _DEBUG
int main()
{
auto tree = new BinarySearchTree<int>();
tree->insert(10);
tree->insert(11);
tree->insert(9);
tree->insert(4);
tree->insert(8);
tree->insert(3);
tree->insert(44);
tree->insert(14);
tree->insert(12);
tree->insert(16);
tree->insert(100);
tree->insert(2);
tree->insert(13);
tree->insert(21);
tree->insert(5);
tree->display_BFS();
assert(destructed_nodes == 0);
cout << "Attempt add duplicate node (10)" << endl;
assert(tree->insert(10) == false);
tree->display_BFS();
cout << "Remove root node (10)" << endl;
tree->remove(10);
tree->display_BFS();
assert(destructed_nodes == -1);
cout << "Attempt remove non existent node (10)" << endl;
assert(tree->remove(10) == false);
tree->display_BFS();
assert(destructed_nodes == -1);
cout << "Remove non root node (13) with right subtree" << endl;
tree->remove(13);
tree->display_BFS();
assert(destructed_nodes == -2);
cout << "Remove non root node (4) with no right subtree" << endl;
tree->remove(4);
tree->display_BFS();
assert(destructed_nodes == -3);
cout << "Add & remove a random node" << endl;
srand(time(0));
int r;
while (true) {
r = rand() % 30;
if (tree->insert(r))
break;
}
tree->display_BFS();
tree->remove(r);
tree->display_BFS();
assert(destructed_nodes == -4);
cout << "Clearing tree" << endl;
tree->clear();
tree->display_BFS();
assert(destructed_nodes == -16);
cout << "[Press enter to exit]" << endl;
cin.ignore();
return 0;
}
#endif