-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeneral_tree.py
More file actions
294 lines (245 loc) · 13.3 KB
/
general_tree.py
File metadata and controls
294 lines (245 loc) · 13.3 KB
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
#! C:\Python36\
# -*- encoding: utf-8 -*-
# This code comes from https://towardsdatascience.com/implementing-the-general-tree-and-depth-first-search-dfs-in-python-from-scratch-b3187e9e117d
# Except for the class general_tree_node
from linked_list import LinkedList
class general_tree_node:
def __init__(self, value):
self.child_node = None
self.parent_node = None
self.value = value
self.right_sib_node = None
self.left_sib_node = None
def get_child(self):
return self.child_node
def get_parent(self):
return self.parent_node
def get_value(self):
return self.value
def get_left(self):
return self.left_sib_node
def get_right(self):
return self.right_sib_node
def set_child(self, child_node):
self.child_node = child_node
def set_parent(self, parent_node):
self.parent_node = parent_node
def set_value(self, value):
self.value = value
def set_left(self, left_sibling_node):
self.left_sib_node = left_sibling_node
def set_right(self, right_sibling_node):
self.right_sib_node = right_sibling_node
class GeneralTree():
"""
Parameters:
@root: root node
@first_born: leftmost child of root node
@current_node: initalized as first born
@current-value: initialized as value of first born (current node)
@visited: Linked list of all values which have previously been visited
@path: FULL path, traversing siblings, from root to search value (LL)
@child_path: path, sibling traversal not captured, more closely resembles typical tree design/behavior (LL)
Methods:
@check_visited: Determines if a tree node has been visited yet to prevent cyclical movements
@check_child_path: Determines if a tree node is already captured in child_path
@depth_first_traversal: Explores entire tree via depth first protocol
@depth_first_search: Captures all necessary node traversals required to move from root to search value
including sibling node traversals
@child_depth_first_search: modification to @depth_first_search such that sibling traversals are eliminated.
This more closely mimics general tree behavior. In a file system, for example, traversing siblings is
not necessary. This method allows for the correct capture of path.
"""
def __init__(self, root=None):
self.root = root # general_tree_node()
self.first_born = self.root.get_child() # general_tree_node()
self.current_node = self.first_born # general_tree_node()
self.current_value = self.current_node.get_value() # general_tree_node.value
self.start = self.current_value # general_tree_node.value
self.visited = LinkedList(self.root.get_value()) # linked list of nodes that are general_tree_node.value
self.path = LinkedList(self.root.get_value()) # linked list of nodes that are general_tree_node.value
self.child_path = LinkedList(self.root.get_value()) # linked list of nodes that are general_tree_node.value
def check_visited(self, val):
if self.visited.find(val):
return True
else:
return False
def check_child_path(self, val):
if self.child_path.find(val):
return True
else:
return False
def depth_first_traversal(self):
if self.current_value == self.root.get_value():
self.visited.insert_at(idx=1, val=self.start)
# copy self.visited then reset for future method calls
self.completed_visited = self.visited
self.current_node = self.first_born
self.current_value = self.current_node.get_value()
self.start = self.current_value
self.visited = LinkedList(self.root.get_value())
return self.completed_visited.dump_list()
else:
# ==== tree traversal logic ====
# If the current NODE has a CHILD that has NOT been visited, set the child node as the current NODE
# and recursively call this function
if self.current_node.get_child() and self.check_visited(self.current_node.get_child().get_value()) == False:
# parent -> child (not yet visited)
self.current_node = self.current_node.get_child()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
# If there is a sibling NODE to the RIGHT, and it has NOT been visited, set the RIGHT NODE as the current
# node and recursively call this function
elif self.current_node.get_right() and self.check_visited(
self.current_node.get_right().get_value()) == False:
# sibling -> right_sibling (not yet visited)
self.current_node = self.current_node.get_right()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
# If there is NO NODE to the RIGHT, but there IS a NODE to the LEFT, then set the LEFT NODE as current,
# and recursively call this function
elif self.current_node.get_right() == None and self.current_node.get_left():
# right_most_sibling -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
# If there is a NODE to the LEFT, and the NODE to the RIGHT has been visited, then set the NODE to the
# LEFT as current, and recursively call this function
elif self.current_node.get_left() != None and self.check_visited(
self.current_node.get_right().get_value()) == True:
# sibling (not left-most or right-most) -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
# OTHERWISE: you're at the LEFT most sibling, so go back to the PARENT node (which has already been visited)
else:
# left_most_sibling -> parent (already visited)
self.current_node = self.current_node.get_parent()
self.current_value = self.current_node.get_value()
# ==== recursively apply logic ====
self.depth_first_traversal()
def depth_first_search(self, search_val):
self.search_val = search_val
if self.current_value == search_val or self.current_value == self.root.get_value():
self.visited.insert_at(idx=1, val=self.start)
self.path.insert_at(idx=1, val=self.start)
if self.check_visited(self.search_val) == True:
condition = 1
else:
condition = 0
# copy self.path and reset for future method calls
self.completed_path = self.path
self.current_node = self.first_born
self.current_value = self.current_node.get_value()
self.start = self.current_value
self.visited = LinkedList(self.root.get_value())
self.path = LinkedList(self.root.get_value())
if condition == 1:
return self.completed_path.dump_list()
else:
print("Value not found")
else:
# ==== tree traversal logic ====
if self.current_node.get_child() and self.check_visited(self.current_node.get_child().get_value()) == False:
# parent -> child (not yet visited)
self.current_node = self.current_node.get_child()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
self.path.append(self.current_value)
elif self.current_node.get_right() and self.check_visited(
self.current_node.get_right().get_value()) == False:
# sibling -> right_sibling (not yet visited)
self.current_node = self.current_node.get_right()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
self.path.append(self.current_value)
elif self.current_node.get_right() == None and self.current_node.get_left():
# right_most_sibling -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
elif self.current_node.get_left() != None and self.check_visited(
self.current_node.get_right().get_value()) == True:
# sibling (not left-most or right-most) -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
self.path.deleteAt(idx=self.path.count)
else:
# left_most_sibling -> parent (already visited)
self.current_node = self.current_node.get_parent()
self.current_value = self.current_node.get_value()
self.path.deleteAt(idx=self.path.count)
# ==== recursively apply logic ====
self.depth_first_search(search_val=self.search_val)
def child_depth_first_search(self, search_val):
self.search_val = search_val
if self.current_value == search_val or self.current_value == self.root.get_value():
self.visited.insert_at(idx=1, val=self.start)
self.path.insert_at(idx=1, val=self.start)
if self.check_visited(self.search_val) == True:
condition = 1
else:
condition = 0
# copy self.path and reset for future method calls
self.completed_child_path = self.child_path
self.current_node = self.first_born
self.current_value = self.current_node.get_value()
self.start = self.current_value
self.visited = LinkedList(self.root.get_value())
self.child_path = LinkedList(self.root.get_value())
if condition == 1:
return self.completed_child_path.dump_list()
else:
print("Value not found")
else:
# ==== tree traversal logic ====
if self.current_node.get_child() and self.check_visited(self.current_node.get_child().get_value()) == False:
# parent -> child (not yet visited)
if self.check_child_path(self.current_node.get_value()) == False:
self.child_path.append(self.current_value)
self.current_node = self.current_node.get_child()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
self.child_path.append(self.current_value)
elif self.current_node.get_right() and self.check_visited(
self.current_node.get_right().get_value()) == False:
# sibling -> right_sibling (not yet visited)
self.child_path.deleteAt(idx=self.child_path.count)
self.current_node = self.current_node.get_right()
self.current_value = self.current_node.get_value()
self.visited.append(self.current_value)
self.child_path.append(self.current_value)
elif self.current_node.get_right() == None and self.current_node.get_left():
# right_most_sibling -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
elif self.current_node.get_left() != None and self.check_visited(
self.current_node.get_right().get_value()) == True:
# sibling (not left-most or right-most) -> left_sibling (already visited)
self.current_node = self.current_node.get_left()
self.current_value = self.current_node.get_value()
self.child_path.deleteAt(idx=self.child_path.count)
else:
# left_most_sibling -> parent (already visited)
self.current_node = self.current_node.get_parent()
self.current_value = self.current_node.get_value()
self.child_path.deleteAt(idx=self.child_path.count)
# ==== recursively apply logic ====
self.child_depth_first_search(search_val=self.search_val)
if __name__ == '__main__':
a1 = general_tree_node(value='a1')
b1 = general_tree_node(value='b1')
b2 = general_tree_node(value='b2')
b3 = general_tree_node(value='b3')
a1.set_child(b1)
b1.set_parent(a1)
b1.set_right(b2)
b2.set_left(b1)
b2.set_right(b3)
b3.set_left(b2)
c1 = general_tree_node(value='c1')
c1.set_parent(b3)
b3.set_child(c1)
d1 = general_tree_node(value='d1')
d1.set_parent(b1)
b1.set_child(d1)
r = GeneralTree(root=a1)
r.depth_first_search(search_val='c1')
r.child_depth_first_search(search_val='c1')