-
Notifications
You must be signed in to change notification settings - Fork 0
/
binary-tree.cpp
261 lines (210 loc) · 7.49 KB
/
binary-tree.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
// TODO NEXT: handle edge cases for BinaryTree:BFS()
// specifically what to do with nil leaves or empty trees
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <iostream>
#include <unordered_map>
#include <cassert>
struct TreeNode {
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int value) {
data = value;
left = nullptr;
right = nullptr;
};
~TreeNode() {
std::cout<<"tree node with data = "<<data<<" destroyed!"<<std::endl;
};
};
class BinaryTree
{
private:
TreeNode* root;
public:
BinaryTree()
{
root = nullptr;
}
BinaryTree(TreeNode* rt)
{
root = rt;
}
TreeNode* get_root() {return root;}
std::vector<TreeNode*> leaves();
// gives a row-by-row, left-to-right traversal of the tree
std::vector<TreeNode*> BFS();
std::vector<TreeNode*> LNR();
std::vector<TreeNode*> NLR();
std::vector<TreeNode*> RNL();
/* Returns the (unique) path from root to node with data=node_value:
{root, intermediate1, intermediate2, ... , node}.
E.g., If node is root itself, returns {root}; If node is root's child, returns {root,node}
Returns empty vector if "to" is not a descendant of "from" */
std::vector<TreeNode*> path(int from_value,int to_value);
std::vector<TreeNode*> path(int node_value);
TreeNode* nodeWith(int value);
int treeHeight();
int treeHeight(TreeNode* root);
void deleteLeaf(int value);
void rightRotate();
void leftRotate();
private:
/* First version (1) pops the top node from the stack and
(2) pushes its children, if any.
Second version (1) marks the top node visited XOR pops the top node if already visited, and
(2) pushes its children, if any.
Both return the next top node, or nullptr if the stack is now empty.
Second version doesn't necessarily remove a node, but it at least discovers children,
or lack of chidren. */
TreeNode* advanceDFS(std::stack<TreeNode*>& visit_stack);
TreeNode* advanceDFS( std::stack<TreeNode*>& visit_stack,
std::unordered_map<TreeNode*,bool>& visited);
};
// BFS returns nodes row by row, from left to right
std::vector<TreeNode*> BinaryTree::BFS()
{
// first-in-first-out queue
std::deque<TreeNode*> queue(1,root);
std::vector<TreeNode*> visited(0);
TreeNode* curr;
while (!queue.empty())
{
curr = queue.front();
if (curr->left)
{
queue.push_back(curr->left);
}
if (curr->right)
{
queue.push_back(curr->right);
}
visited.push_back(curr);
queue.pop_front();
}
return visited;
}
// returns -1 for empty tree, 0 for the tree with single node, 1 for a tree with two nodes, etc.
int BinaryTree::treeHeight()
{
if (!root) return -1;
// does a dfs, while maintaining a depths map
std::unordered_map<TreeNode*,int> depths {{root,0}};
std::vector<TreeNode*> to_be_visited_stack {root};
TreeNode* curr_node;
int curr_height,record_height=0;
while (!to_be_visited_stack.empty())
{
curr_node=to_be_visited_stack.back();
curr_height=depths[curr_node];
to_be_visited_stack.pop_back();
if (!curr_node->left && !curr_node->right)
// we are at a leaf; depth of leaf might be the tree height;
record_height=std::max(record_height,curr_height);
if (curr_node->right)
{
depths[curr_node->right]=curr_height+1;
to_be_visited_stack.push_back(curr_node->right);
}
if (curr_node->left)
{
depths[curr_node->left]=curr_height+1;
to_be_visited_stack.push_back(curr_node->left);
}
}
return record_height;
}
void BinaryTree::rightRotate()
{
if (!root) {std::cout<<"WARNING: empty tree; nothing to rotate"<<std::endl; return;}
if (!root->left) {std::cout<<"not enough nodes to rightRotate!"<<std::endl; return;}
TreeNode *x=root, *y=x->left, *z=y->right;
x->left=z;
y->right=x;
root=y;
}
void BinaryTree::leftRotate()
{
if (!root) {std::cout<<"WARNING: empty tree; nothing to rotate"<<std::endl; return;}
if (!root->right) {std::cout<<"not enough nodes to leftRotate!"<<std::endl; return;}
TreeNode *x=root, *y=x->right, *z=y->left;
x->right=z;
y->left=x;
root=y;
}
TreeNode* BinaryTree::advanceDFS(std::stack<TreeNode*>& visit_stack)
{
if ( visit_stack.empty() ) return nullptr;
TreeNode* curr_node = visit_stack.top();
visit_stack.pop();
if (curr_node->right) visit_stack.push(curr_node->right);
if (curr_node->left) visit_stack.push(curr_node->left);
if ( visit_stack.empty() ) return nullptr;
return visit_stack.top();
}
TreeNode* BinaryTree::advanceDFS( std::stack<TreeNode*>& visit_stack,
std::unordered_map<TreeNode*,bool>& visited)
{
if ( visit_stack.empty() ) return nullptr;
TreeNode* curr_node = visit_stack.top();
if ( visited[curr_node] )
{
// Must have already seen this node and added its children earlier
visit_stack.pop();
} else {
// First time visiting this node, so we never added its children before
visited[curr_node] = true;
if (curr_node->right) visit_stack.push(curr_node->right);
if (curr_node->left) visit_stack.push(curr_node->left);
}
if ( visit_stack.empty() ) return nullptr;
return visit_stack.top();
}
std::vector<TreeNode*> BinaryTree::path(int from_value, int to_value)
{
std::unordered_map<TreeNode*,bool> visited{};
std::stack<TreeNode*> visit_stack = {};
visit_stack.push(root);
// first locate the from_node:
TreeNode* from_node = visit_stack.top();
while ( !visit_stack.empty() )
{
if (from_node->data==from_value) {
break;
}
from_node = advanceDFS(visit_stack,visited);
}
// in from_value is not in the tree, the whole tree would have been traversed:
if ( visit_stack.empty() ) return std::vector<TreeNode*>{};
// after having found from_node, find to_node; it should not require backtracking past from_node
// since to_node has to be a descendant of from_node;
if (to_value==from_value) return std::vector<TreeNode*>{from_node};
TreeNode* to_node = advanceDFS(visit_stack,visited);
while ( to_node!=from_node )
{
if (to_node->data==to_value) {
break;
}
to_node = advanceDFS(visit_stack,visited);
}
if (to_node==from_node) return std::vector<TreeNode*>{};
std::cout<<"found to node!"<<std::endl;
// Path = sequence of nodes from to_node down to from_node in the stack,
// which have been marked visited.
std::vector<TreeNode*> result_in_reverse {to_node};
for (TreeNode* curr_node = visit_stack.top(); curr_node!= from_node; curr_node = visit_stack.top() )
{
if (visited[curr_node]) result_in_reverse.push_back(curr_node);
visit_stack.pop();
}
result_in_reverse.push_back(from_node);
return std::vector<TreeNode*>(result_in_reverse.rbegin(),result_in_reverse.rend());
}
std::vector<TreeNode*> BinaryTree::path(int node_value)
{
TreeNode* fromNode = root;
return path(root->data, node_value);
}