-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathavl.py
352 lines (300 loc) · 10.6 KB
/
avl.py
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''Defines an AVL tree structure and its common operations
'''
import random
from avlnode import AVLNode
import logging
__author__ = "Ricardo J. Rodríguez"
__copyright__ = "Copyright 2020, University of Zaragoza, Spain"
__credits__ = ["Ricardo J. Rodríguez"]
__license__ = "GPL"
__version__ = "1.0"
__maintainer__ = "Ricardo J. Rodríguez"
__email__ = "[email protected]"
__status__ = "Production"
def debug(msg):
logging.debug(msg)
class AVLTree():
def __init__(self):
self.root = None
def insert(self, key, value=None, duplicated_keys=False):
'''
Insert a new node in the AVL tree
Receive a parameter to indicate whether duplicated keys are allowed or not
'''
newnode = AVLNode(key, value)
debug("[+] New node created: [{0}; {1}]".format(key, value))
self.root = self._insert(self.root, newnode, duplicated_keys)
def _insert(self, root: AVLNode, newnode: AVLNode, duplicated_keys=False) -> AVLNode:
debug("[+] Inserting {0} (root: {1})".format(newnode, root))
if not root: # base case, no tree
return newnode
elif newnode.key < root.key: # insert at the left, if the new node is smaller
debug("[+] Going left")
root.left = self._insert(root.left, newnode, duplicated_keys)
elif newnode.key > root.key: # if greater, insert at the right
debug("[+] Going right")
root.right = self._insert(root.right, newnode, duplicated_keys)
else:
if duplicated_keys:
# duplicated keys allowed. We just fuse the values in a list
if type(root.value) is not list:
aux = root.value
root.value = []
root.value.append(aux)
root.value.append(newnode.value)
else:
return root # no duplicates allowed
# update height after insertion
root.update_height()
debug("[+] Updated height of node {0}".format(root))
return self._rebalance(root)
def _rebalance(self, root: AVLNode) -> AVLNode:
'''
Rebalance operations for a given tree node
'''
bf = root.get_balance_factor()
if bf > 1:
if root.right.get_height(root.right.right) > root.right.get_height(root.right.left): # left-left imbalance
root = self._left_rotate(root)
else: # right-left
root = self._right_left_rotate(root)
elif bf < -1:
if root.left.get_height(root.left.left) > root.left.get_height(root.left.right): # right-right imbalance
root = self._right_rotate(root)
else: # left-rigth
root = self._left_right_rotate(root)
return root
def _right_rotate(self, root: AVLNode) -> AVLNode:
debug("[+] Right rotation ")
tmp = root.left
root.left = tmp.right
tmp.right = root
root.update_height()
tmp.update_height()
return tmp
def _left_right_rotate(self, root: AVLNode) -> AVLNode:
debug("[*] Left-Right rotation ")
root.left = self._left_rotate(root.left)
return self._right_rotate(root)
def _left_rotate(self, root: AVLNode) -> AVLNode:
debug("[+] Left rotation ")
tmp = root.right
root.right = tmp.left
tmp.left = root
root.update_height()
tmp.update_height()
return tmp
def _right_left_rotate(self, root: AVLNode) -> AVLNode:
debug("[+] Right-Left rotation ")
root.right = self._right_rotate(root.right)
return self._left_rotate(root)
def _remove(self, root: AVLNode, key) -> AVLNode:
# search the element, like a BST
if root is None: # element not found
return None
elif key < root.key:
root.left = self._remove(root.left, key)
elif key > root.key:
root.right = self._remove(root.right, key)
else: # element found
# check number of childrens
if root.left is None and root.right is None:
# no children
return None
elif root.left is None:
# right children only
root = root.right
elif root.right is None:
# left children only
root = root.left
else:
# two children
aux = self.find_min(root.right)
root.update_content(aux)
root.right = self.remove(root.right, aux.key)
# update heights
root.update_height()
if root is not None:
root = self._rebalance(root)
return root
def remove(self, key):
'''
Remove the node with key in the current tree, if exists
'''
if self.root is None:
print('[-] AVL tree is empty!')
else:
self.root = self._remove(self.root, key)
def _display(self, node: AVLNode, level=0, prefix=''):
if node != None:
print('{0}{1}{2}'.format('-'*level, prefix, node))
if node.left != None:
self._display(node.left, level + 1, '<')
if node.right != None:
self._display(node.right, level + 1, '>')
def display(self):
'''
Display the current tree, using recursion
'''
debug("[+] Displaying the AVL ...")
if self.root != None:
self._display(self.root)
else:
print('[-] AVL tree is empty!')
def _post_order(self, node: AVLNode):
_str = ""
if node is not None:
_str = _str + self._post_order(node.left)
_str = _str + self._post_order(node.right)
_str = str(_str) + str(node) + ';'
return _str
def post_order(self, print_to_stdout=True):
'''
Traverse the current tree in post-order, using recursion. Return a string containing it
As optional, it accepts a boolean to print the content to stdout.
'''
_str = self._post_order(self.root)
if print_to_stdout:
print(_str)
return _str
def _pre_order(self, node: AVLNode):
_str = ""
if node is not None:
_str = _str + str(node) + ';'
_str = _str + self._pre_order(node.left)
_str = _str + self._pre_order(node.right)
return _str
def pre_order(self, print_to_stdout=True):
'''
Traverse the current tree in pre-order, using recursion. Return a string containing it
As optional, it accepts a boolean to print the content to stdout.
'''
_str = self._pre_order(self.root)
if print_to_stdout:
print(_str)
return _str
def _in_order(self, node: AVLNode):
_str = ""
if node is not None:
_str = _str + self._in_order(node.left)
_str = _str + str(node) + ';'
_str = _str + self._in_order(node.right)
return _str
def in_order(self, print_to_stdout=True):
'''
Traverse the current tree in order, using recursion. Return a string containing it
As optional, it accepts a boolean to print the content to stdout.
'''
_str = self._in_order(self.root)
if print_to_stdout:
print(_str)
return _str
def _find_max(self, root: AVLNode) -> AVLNode:
if root.right is None:
return root
else:
return self._find_max(root.right)
def find_max(self) -> AVLNode:
'''
Find the maximum key value of the current tree
'''
if self.root is None:
print('[-] AVL tree is empty!')
return None
else:
return self._find_max(self.root)
def _find_min(self, root: AVLNode) -> AVLNode:
if root.left is None:
return root
else:
return self._find_min(root.left)
def find_min(self) -> AVLNode:
'''
Find the minimum key value of the current tree
'''
if self.root is None:
print('[-] AVL tree is empty!')
return None
else:
return self._find_min(self.root)
def _search(self, root: AVLNode, key) -> AVLNode:
if root is None:
return None
elif key < root.key:
return self._search(root.left, key)
elif key > root.key:
return self._search(root.right, key)
else: # otherwise, root is the element
return root
def search(self, key) -> AVLNode:
'''
Search a given key in the current tree
'''
if self.root is None:
print('[-] AVL tree is empty!')
return None
else:
return self._search(self.root, key)
def exists(self, key) -> bool:
'''
Check whether a given key exists in the current tree
'''
if self.root is None:
print('[-] AVL tree is empty!')
return False
else:
return (self._search(self.root, key) is not None)
def get_height(self) -> int:
'''
Return the height of the current tree
'''
if self.root is None:
return 0
else:
return self.root.get_height()
def _get_count(self, root: AVLNode) -> int:
if root is None:
return 0
count = 1
if root.left:
count = count + self._get_count(root.left)
if root.right:
count = count + self._get_count(root.right)
return count
def get_count(self) -> int:
'''
Return the number of nodes of the current tree
'''
return self._get_count(self.root)
# unit test
if __name__ == "__main__":
a = AVLTree()
print("[*] Inserting random data ...")
#randomlist = random.sample(range(0, 30), 5)
randomlist = [18, 1, 13, 12, 3]
print("[*] Data: " + str(randomlist))
for i in randomlist:
a.insert(i)
a.insert(18, duplicated_keys=True)
a.display()
a.in_order()
#import pdb; pdb.set_trace()
a.pre_order()
a.post_order()
print('Minimum key node: ' + str(a.find_min()))
print('Maximum key node: ' + str(a.find_max()))
print('Exists? ' + str(a.exists(randomlist[0])))
print('Exists? ' + str(a.exists(-1)))
a.remove(18)
a.display()
a.remove(3)
a.display()
a.insert(5)
a.display()
a.insert(3)
a.display()
a.insert(4)
a.display()
print('Number of nodes: ' + str(a.get_count()))