Skip to content

Commit fd98e99

Browse files
committed
ported AVL Tree code
1 parent 1515e07 commit fd98e99

File tree

1 file changed

+329
-0
lines changed

1 file changed

+329
-0
lines changed

AVL_Tree/main.py

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
class node:
2+
def __init__(self,value=None):
3+
self.value=value
4+
self.left_child=None
5+
self.right_child=None
6+
self.parent=None # pointer to parent node in tree
7+
self.height=1 # height of node in tree (max dist. to leaf) NEW FOR AVL
8+
9+
class AVLTree:
10+
def __init__(self):
11+
self.root=None
12+
13+
def __repr__(self):
14+
if self.root==None: return ''
15+
content='\n' # to hold final string
16+
cur_nodes=[self.root] # all nodes at current level
17+
cur_height=self.root.height # height of nodes at current level
18+
sep=' '*(2**(cur_height-1)) # variable sized separator between elements
19+
while True:
20+
cur_height+=-1 # decrement current height
21+
if len(cur_nodes)==0: break
22+
cur_row=' '
23+
next_row=''
24+
next_nodes=[]
25+
26+
if all(n is None for n in cur_nodes):
27+
break
28+
29+
for n in cur_nodes:
30+
31+
if n==None:
32+
cur_row+=' '+sep
33+
next_row+=' '+sep
34+
next_nodes.extend([None,None])
35+
continue
36+
37+
if n.value!=None:
38+
buf=' '*int((5-len(str(n.value)))/2)
39+
cur_row+='%s%s%s'%(buf,str(n.value),buf)+sep
40+
else:
41+
cur_row+=' '*5+sep
42+
43+
if n.left_child!=None:
44+
next_nodes.append(n.left_child)
45+
next_row+=' /'+sep
46+
else:
47+
next_row+=' '+sep
48+
next_nodes.append(None)
49+
50+
if n.right_child!=None:
51+
next_nodes.append(n.right_child)
52+
next_row+='\ '+sep
53+
else:
54+
next_row+=' '+sep
55+
next_nodes.append(None)
56+
57+
content+=(cur_height*' '+cur_row+'\n'+cur_height*' '+next_row+'\n')
58+
cur_nodes=next_nodes
59+
sep=' '*int(len(sep)/2) # cut separator size in half
60+
return content
61+
62+
def insert(self,value):
63+
if self.root==None:
64+
self.root=node(value)
65+
else:
66+
self._insert(value,self.root)
67+
68+
def _insert(self,value,cur_node):
69+
if value<cur_node.value:
70+
if cur_node.left_child==None:
71+
cur_node.left_child=node(value)
72+
cur_node.left_child.parent=cur_node # set parent
73+
self._inspect_insertion(cur_node.left_child)
74+
else:
75+
self._insert(value,cur_node.left_child)
76+
elif value>cur_node.value:
77+
if cur_node.right_child==None:
78+
cur_node.right_child=node(value)
79+
cur_node.right_child.parent=cur_node # set parent
80+
self._inspect_insertion(cur_node.right_child)
81+
else:
82+
self._insert(value,cur_node.right_child)
83+
else:
84+
print("Value already in tree!")
85+
86+
def print_tree(self):
87+
if self.root!=None:
88+
self._print_tree(self.root)
89+
90+
def _print_tree(self,cur_node):
91+
if cur_node!=None:
92+
self._print_tree(cur_node.left_child)
93+
print ('%s, h=%d'%(str(cur_node.value),cur_node.height))
94+
self._print_tree(cur_node.right_child)
95+
96+
def height(self):
97+
if self.root!=None:
98+
return self._height(self.root,0)
99+
else:
100+
return 0
101+
102+
def _height(self,cur_node,cur_height):
103+
if cur_node==None: return cur_height
104+
left_height=self._height(cur_node.left_child,cur_height+1)
105+
right_height=self._height(cur_node.right_child,cur_height+1)
106+
return max(left_height,right_height)
107+
108+
def find(self,value):
109+
if self.root!=None:
110+
return self._find(value,self.root)
111+
else:
112+
return None
113+
114+
def _find(self,value,cur_node):
115+
if value==cur_node.value:
116+
return cur_node
117+
elif value<cur_node.value and cur_node.left_child!=None:
118+
return self._find(value,cur_node.left_child)
119+
elif value>cur_node.value and cur_node.right_child!=None:
120+
return self._find(value,cur_node.right_child)
121+
122+
def delete_value(self,value):
123+
return self.delete_node(self.find(value))
124+
125+
def delete_node(self,node):
126+
127+
## -----
128+
# Improvements since prior lesson
129+
130+
# Protect against deleting a node not found in the tree
131+
if node==None or self.find(node.value)==None:
132+
print("Node to be deleted not found in the tree!")
133+
return None
134+
## -----
135+
136+
# returns the node with min value in tree rooted at input node
137+
def min_value_node(n):
138+
current=n
139+
while current.left_child!=None:
140+
current=current.left_child
141+
return current
142+
143+
# returns the number of children for the specified node
144+
def num_children(n):
145+
num_children=0
146+
if n.left_child!=None: num_children+=1
147+
if n.right_child!=None: num_children+=1
148+
return num_children
149+
150+
# get the parent of the node to be deleted
151+
node_parent=node.parent
152+
153+
# get the number of children of the node to be deleted
154+
node_children=num_children(node)
155+
156+
# break operation into different cases based on the
157+
# structure of the tree & node to be deleted
158+
159+
# CASE 1 (node has no children)
160+
if node_children==0:
161+
162+
if node_parent!=None:
163+
# remove reference to the node from the parent
164+
if node_parent.left_child==node:
165+
node_parent.left_child=None
166+
else:
167+
node_parent.right_child=None
168+
else:
169+
self.root=None
170+
171+
# CASE 2 (node has a single child)
172+
if node_children==1:
173+
174+
# get the single child node
175+
if node.left_child!=None:
176+
child=node.left_child
177+
else:
178+
child=node.right_child
179+
180+
if node_parent!=None:
181+
# replace the node to be deleted with its child
182+
if node_parent.left_child==node:
183+
node_parent.left_child=child
184+
else:
185+
node_parent.right_child=child
186+
else:
187+
self.root=child
188+
189+
# correct the parent pointer in node
190+
child.parent=node_parent
191+
192+
# CASE 3 (node has two children)
193+
if node_children==2:
194+
195+
# get the inorder successor of the deleted node
196+
successor=min_value_node(node.right_child)
197+
198+
# copy the inorder successor's value to the node formerly
199+
# holding the value we wished to delete
200+
node.value=successor.value
201+
202+
# delete the inorder successor now that it's value was
203+
# copied into the other node
204+
self.delete_node(successor)
205+
206+
# exit function so we don't call the _inspect_deletion twice
207+
return
208+
209+
if node_parent!=None:
210+
# fix the height of the parent of current node
211+
node_parent.height=1+max(self.get_height(node_parent.left_child),self.get_height(node_parent.right_child))
212+
213+
# begin to traverse back up the tree checking if there are
214+
# any sections which now invalidate the AVL balance rules
215+
self._inspect_deletion(node_parent)
216+
217+
def search(self,value):
218+
if self.root!=None:
219+
return self._search(value,self.root)
220+
else:
221+
return False
222+
223+
def _search(self,value,cur_node):
224+
if value==cur_node.value:
225+
return True
226+
elif value<cur_node.value and cur_node.left_child!=None:
227+
return self._search(value,cur_node.left_child)
228+
elif value>cur_node.value and cur_node.right_child!=None:
229+
return self._search(value,cur_node.right_child)
230+
return False
231+
232+
233+
# Functions added for AVL...
234+
235+
def _inspect_insertion(self,cur_node,path=[]):
236+
if cur_node.parent==None: return
237+
path=[cur_node]+path
238+
239+
left_height =self.get_height(cur_node.parent.left_child)
240+
right_height=self.get_height(cur_node.parent.right_child)
241+
242+
if abs(left_height-right_height)>1:
243+
path=[cur_node.parent]+path
244+
self._rebalance_node(path[0],path[1],path[2])
245+
return
246+
247+
new_height=1+cur_node.height
248+
if new_height>cur_node.parent.height:
249+
cur_node.parent.height=new_height
250+
251+
self._inspect_insertion(cur_node.parent,path)
252+
253+
def _inspect_deletion(self,cur_node):
254+
if cur_node==None: return
255+
256+
left_height =self.get_height(cur_node.left_child)
257+
right_height=self.get_height(cur_node.right_child)
258+
259+
if abs(left_height-right_height)>1:
260+
y=self.taller_child(cur_node)
261+
x=self.taller_child(y)
262+
self._rebalance_node(cur_node,y,x)
263+
264+
self._inspect_deletion(cur_node.parent)
265+
266+
def _rebalance_node(self,z,y,x):
267+
if y==z.left_child and x==y.left_child:
268+
self._right_rotate(z)
269+
elif y==z.left_child and x==y.right_child:
270+
self._left_rotate(y)
271+
self._right_rotate(z)
272+
elif y==z.right_child and x==y.right_child:
273+
self._left_rotate(z)
274+
elif y==z.right_child and x==y.left_child:
275+
self._right_rotate(y)
276+
self._left_rotate(z)
277+
else:
278+
raise Exception('_rebalance_node: z,y,x node configuration not recognized!')
279+
280+
def _right_rotate(self,z):
281+
sub_root=z.parent
282+
y=z.left_child
283+
t3=y.right_child
284+
y.right_child=z
285+
z.parent=y
286+
z.left_child=t3
287+
if t3!=None: t3.parent=z
288+
y.parent=sub_root
289+
if y.parent==None:
290+
self.root=y
291+
else:
292+
if y.parent.left_child==z:
293+
y.parent.left_child=y
294+
else:
295+
y.parent.right_child=y
296+
z.height=1+max(self.get_height(z.left_child),
297+
self.get_height(z.right_child))
298+
y.height=1+max(self.get_height(y.left_child),
299+
self.get_height(y.right_child))
300+
301+
def _left_rotate(self,z):
302+
sub_root=z.parent
303+
y=z.right_child
304+
t2=y.left_child
305+
y.left_child=z
306+
z.parent=y
307+
z.right_child=t2
308+
if t2!=None: t2.parent=z
309+
y.parent=sub_root
310+
if y.parent==None:
311+
self.root=y
312+
else:
313+
if y.parent.left_child==z:
314+
y.parent.left_child=y
315+
else:
316+
y.parent.right_child=y
317+
z.height=1+max(self.get_height(z.left_child),
318+
self.get_height(z.right_child))
319+
y.height=1+max(self.get_height(y.left_child),
320+
self.get_height(y.right_child))
321+
322+
def get_height(self,cur_node):
323+
if cur_node==None: return 0
324+
return cur_node.height
325+
326+
def taller_child(self,cur_node):
327+
left=self.get_height(cur_node.left_child)
328+
right=self.get_height(cur_node.right_child)
329+
return cur_node.left_child if left>=right else cur_node.right_child

0 commit comments

Comments
 (0)