im trying to implement insertion in AVLtree , the insertion works on small number of insertions , but when i try random insert there is a loop in node_position , a small number of insertions with small valued numbers there is no loops or errors , there is a virtual node for every son that is None ( this is instructions from the homework ) , first i find the position to insert then i do insertion and rotations ...
here is my code :import randomclass AVLNode(object):"""Constructor, you are allowed to add more fields.
@type key: int or None@type value: any@param value: data of your node"""def __init__(self, key, value): self.key = key self.value = value self.left = None self.right = None self.parent = None self.height = -1"""returns the left child@rtype: AVLNode@returns: the left child of self, None if there is no left child (if self is virtual)"""def get_left(self): return self.left if self.is_real_node() else None"""returns the right child@rtype: AVLNode@returns: the right child of self, None if there is no right child (if self is virtual)"""def get_right(self): return self.right if self.is_real_node() else None"""returns the parent @rtype: AVLNode@returns: the parent of self, None if there is no parent"""def get_parent(self): return self.parent"""returns the key@rtype: int or None@returns: the key of self, None if the node is virtual"""def get_key(self): return self.key if self.is_real_node() else None"""returns the value@rtype: any@returns: the value of self, None if the node is virtual"""def get_value(self): return self.value if self.is_real_node() else None"""returns the height@rtype: int@returns: the height of self, -1 if the node is virtual"""def get_height(self): return self.height if self.is_real_node() else -1"""sets left child@type node: AVLNode@param node: a node"""def set_left(self, node): self.left = node node.parent = self"""sets right child@type node: AVLNode@param node: a node"""def set_right(self, node): self.right = node node.parent = self"""sets parent@type node: AVLNode@param node: a node"""def set_parent(self, node): self.parent = node"""sets key@type key: int or None@param key: key"""def set_key(self, key): self.key = key"""sets value@type value: any@param value: data"""def set_value(self, value): self.value = value"""sets the height of the node@type h: int@param h: the height"""def set_height(self, h): self.height = h"""returns whether self is not a virtual node @rtype: bool@returns: False if self is a virtual node, True otherwise."""def is_real_node(self): return self.key is not None""" Calculate BlanceFactor and returns it @rtype: int @returns : BalanceFactor """"""A class implementing the ADT Dictionary, using an AVL tree."""
class AVLTree(object):
"""Constructor, you are allowed to add more fields. """def __init__(self): self.root = None self.size = 0 # add your fields here"""searches for a value in the dictionary corresponding to the key@type key: int@param key: a key to be searched@rtype: any@returns: the value corresponding to key."""def search(self, key): if self.root is None : return None return self.search_helper(self.root, key)def search_helper(self , node ,key): if not node.is_real_node() : return None Node_key = node.get_key() if Node_key == key: return node if key < Node_key: return self.search_helper(node.get_left(), key) return self.search_helper(node.get_right(), key)"""inserts val at position i in the dictionary@type key: int@pre: key currently does not appear in the dictionary@param key: key of item that is to be inserted to self@type val: any@param val: the value of the item@rtype: int@returns: the number of rebalancing operation due to AVL rebalancing"""def insert(self, key, val): node = AVLNode(key, val) node.set_height(0) vert1 = AVLNode(None, None) vert2 = AVLNode(None, None) if self.root is None: self.root = node self.root.set_left(vert1) self.root.set_right(vert2) self.size+=1 return 0 self.regular_insert(node) p = node.get_parent() son = node self.size += 1 node.set_left(vert1) node.set_right(vert2) return self.Balance(p, son)def node_position(self, node, key): # look for key "key" in the subtree of node # return the last node encountered while node is not None and node.key is not None: curr = node if key == node.key: return node elif key < node.key: node = node.left else: node = node.right return currdef regular_insert(self, node): new_p = self.node_position(self.root, node.key) # new parent node.set_parent(new_p) if node.key < new_p.key: new_p.set_left(node) else: new_p.set_right(node) returndef BF(self, node): # returns the balance factor of a node h1 = node.get_left().get_height() if node.get_left() is not None else -1 h2 = node.get_right().get_height() if node.get_right() is not None else -1 return h1 - h2def height_has_changed(self, node, son): # checks if node`s height has changed after insertion return node.get_height() < son.get_height() + 1def rotate(self, node, son): # performs rotation n_bf = self.BF(node) s_bf = self.BF(son) if n_bf > 0 and s_bf > 0: self.do_right_rotation(node, son) return 1 if n_bf < 0 and s_bf < 0: self.do_left_rotation(node, son) return 1 if n_bf > 0 and s_bf < 0: grandson = son.get_right() self.do_left_rotation(son, grandson) self.do_right_rotation(node, grandson) grandson.set_height(grandson.get_height() + 1) return 2 if n_bf < 0 and s_bf > 0: grandson = son.get_left() self.do_right_rotation(son, grandson) self.do_left_rotation(node, grandson) grandson.set_height(grandson.get_height() + 1) return 2 return 0def do_left_rotation(self, node, son): if self.root == node: self.root = son node.set_height(node.get_height() - 2) grandson = son.get_left() node.set_right(grandson) grandson.set_parent(node) node_p = node.get_parent() son.set_left(node) son.set_parent(node_p) if node_p is not None: if node_p.get_left() == node: node_p.set_left(son) else: node_p.set_right(son) node.set_parent(son) returndef do_right_rotation(self, node, son): if self.root == node: self.root = son node.set_height(node.get_height() - 2) node_p = node.get_parent() grandson = son.get_right() node.set_left(grandson) grandson.set_parent(node) son.set_right(node) son.set_parent(node_p) if node_p is not None: if node_p.get_left() == node: node_p.set_left(son) else: node_p.set_right(son) node.set_parent(son) returndef Balance(self, p, son): balance = 0 # to compute the number of balancing operations while p is not None: bf = self.BF(p) h_changed = self.height_has_changed(p, son) if (abs(bf) < 2) and (h_changed is False): return balance if (abs(bf) < 2) and h_changed: p.set_height(p.get_height() + 1) balance += 1 son = p p = p.get_parent() else: p.set_height(p.get_height() + 1) balance += 1 balance += self.rotate(p, son) return balance return balance""" Rebalnce@pre : node in self @rtype:int @returns: Number of rebalancing operations including fixing height and rotations"""def Deletion_Rebalance(self, node): if node is None: return 0 # this means that we deleted a root, we can't modify its height so we return 0 count = 0 while node is not None: original_height = node.get_height() node_parent = node.get_parent() self.height_Update(node) right_son_BF = self.BF(node.get_right()) left_son_BF = self.BF(node.get_left()) BF = self.BF(node) if (abs(BF)) < 2 : if original_height == node.get_height() : # case 3.2 break else : count+=1 else: if BF == -2: if right_son_BF == -1 or right_son_BF == 0: self.do_left_rotation(node , node.get_right()) count += 1 elif right_son_BF == 1: self.do_right_rotation(node.get_right() , node.get_right().get_left()) self.do_left_rotation(node , node.get_right()) count += 2 elif BF == 2: left_node = node.get_left() if left_son_BF == -1: self.do_left_rotation(node.get_left(),node.get_left().get_right()) self.do_right_rotation(node, node.get_left()) count += 2 elif left_son_BF == 1 or left_son_BF == 0: self.do_right_rotation(node , node.get_left()) count += 1 node = node_parent return count""" find the successor of node @pre : node in self @rtype: AVL node@returns: the successor of node"""def successor (self,node) : if node.get_right().is_real_node(): return self.Min(node.get_right()) temp = node.get_parent() while temp is not None and node == temp.right : node = temp temp = node.get_parent() return temp""" find the min @pre: node is in self@returns : the node with the min key in node path @:rtype AVL node"""def Min(self,node): temp = node while temp.get_left().is_real_node() : temp = temp.get_left() return temp""" updates node's height @pre: node is in self"""def height_Update(self, node): left_height = node.get_left().get_height() if node.is_real_node() else -1 right_height = node.get_right().get_height() if node.is_real_node() else -1 node.set_height(max(left_height, right_height) + 1 )"""deletes node from the dictionary@type node: AVLNode@pre: node is a real pointer to a node in self@rtype: int@returns: the number of rebalancing operation due to AVL rebalancing"""def delete(self, node): return self.delete_node(node)""" deleting node as usual in BST and fixing size @pre : node is in self @rtype : int @returns : the number of rebalancing operation due to AVL rebalancing"""def delete_node(self,node): flag= False node_parent = node.get_parent() node_right = node.get_right() node_left = node.get_left() virtual_Node = AVLNode(None, None) if not node_right.is_real_node() and not node_left.is_real_node() : if node_parent is None: self.root = None else : # node is not a root if node_parent.get_left() == node : node_parent.set_left(virtual_Node) else : node_parent.set_right(virtual_Node) elif not node_right.is_real_node() and node_left.is_real_node() : # case 2 : ond child if node_parent is None: # Root node self.root = node_left elif node_parent.get_left() == node: node_parent.set_left(node_left) else: node_parent.set_right(node_left) elif node_right.is_real_node() and not node_left.is_real_node() : # just bypass it , note that we didnt use set functions implemented in AVLnode class because there we add 1 to height if node_parent is None: # Root node self.root = node_right elif node_parent.left == node: node_parent.left = node_right node_parent.right = virtual_Node else: node_parent.right = node_right node_parent.left = virtual_Node else : # case 2 : two children flag=True successor_node = self.successor(node) # Find the successor node successor_node_parent = successor_node.get_parent() node.set_value(successor_node.get_value()) node.set_key((successor_node.get_key())) if node_parent is None : self.root=node self.root.set_left(node.left) self.root.set_right(node.right) elif successor_node_parent.left == successor_node : successor_node_parent.set_left(virtual_Node) else : successor_node_parent.set_right(virtual_Node) return self.delete_node(successor_node) if not flag : # if flag is false , we didnt delete successor node recursively , thus size updated once not twice self.size = self.size - 1 # update size if node_parent is not None and abs(self.BF(node_parent)) ==2 : return self.Deletion_Rebalance(node_parent)"""returns an array representing dictionary @rtype: list@returns: a sorted list according to key of touples (key, value) representing the data structure"""def avl_to_array(self): arr = [] def avl_to_array_helper(node,arr): if not node.is_real_node() : return avl_to_array_helper(node.left,arr) arr.append((node.get_key(),node.get_value())) avl_to_array_helper(node.right, arr) avl_to_array_helper(self.root,arr) return arr"""returns the number of items in dictionary @rtype: int@returns: the number of items in dictionary """def size(self): # Simply return size field return self.size"""splits the dictionary at the i'th index@type node: AVLNode@pre: node is in self@param node: The intended node in the dictionary according to whom we split@rtype: list@returns: a list [left, right], where left is an AVLTree representing the keys in the dictionary smaller than node.key, right is an AVLTree representing the keys in the dictionary larger than node.key."""def split(self, node): cost = 0 counter = 0 right_tree = AVLTree() left_tree = AVLTree() current = self.search(node.get_key()) left_tree.root=current.get_left() if current.get_left().is_real_node() else None right_tree.root=current.get_right() if current.get_right().is_real_node() else None if current == self.root : return[left_tree,right_tree] cost, counter = self.Left_Subtree(current.get_parent(), left_tree, node.get_key(),cost, counter) cost, counter = self.Right_Subtree(current.get_parent(), right_tree, node.get_key(),cost, counter) print("cost", cost) print("counter", counter) return [left_tree, right_tree]def Left_Subtree(self, n1, left_tree,node_key, cost, counter): while n1 is not None: if n1.get_key()< node_key: _leftSubtree = AVLTree() if left_tree.root is None : left_tree.insert(n1.get_key(),n1.get_value()) elif not n1.get_left().is_real_node() : left_tree.insert(n1.get_key(),n1.get_value()) else: _leftSubtree.root= n1.get_left() cost += left_tree.join(_leftSubtree, n1.get_key(), n1.get_value()) counter += 1 n1 = n1.get_parent() print(cost) return cost, counterdef Right_Subtree(self, n2, right_tree, node_key, cost, counter): while n2 is not None: if n2.get_key() > node_key: _rightSubtree = AVLTree() if right_tree.root is None: right_tree.insert(n2.get_key(), n2.get_value()) elif not n2.get_left().is_real_node() : right_tree.insert(n2.get_key(), n2.get_value()) else: _rightSubtree.root = n2.get_left() cost += right_tree.join(_rightSubtree, n2.get_key(), n2.get_value()) counter += 1 n2 = n2.get_parent() return cost, counter"""joins self with key and another AVLTree@type tree2: AVLTree @param tree2: a dictionary to be joined with self@type key: int @param key: The key separting self with tree2@type val: any @param val: The value attached to key@pre: all keys in self are smaller than key and all keys in tree2 are larger than key@rtype: int@returns: the absolute value of the difference between the height of the AVL trees joined"""def join(self, tree2, key, val): print("here?") x = AVLNode(key, val) t1_h = self.root.get_height() t2_h = tree2.root.get_height() t1_k = self.root.get_key() t2_k = tree2.root.get_key() if (t1_h == t2_h) and (t1_k < t2_k): x.set_left(self.root) x.set_right(tree2.root) x.set_height(t1_h + 1) self.root = x elif (t1_h == t2_h) and (t1_k > t2_k): x.set_right(self.root) x.set_left(tree2.root) x.set_height(t1_h + 1) self.root = x elif (t1_h < t2_h) and (t1_k < t2_k): t1 = self t2 = tree2 self.add(t1, t2, x, True) # the boolean tells if self`s keys are bigger than key elif (t1_h >= t2_h) and (t1_k > t2_k): t1 = tree2 t2 = self self.add(t1, t2, x, True) elif (t1_h > t2_h) and (t1_k < t2_k): t1 = tree2 t2 = self self.add(t1, t2, x, False) elif (t1_h < t2_h) and (t1_k > t2_k): t1 = self t2 = tree2 self.add(t1, t2, x, False) self.size = self.size + tree2.size + 1 return abs(t1_h - t2_h) + 1def add(self, t1, t2, x, add_to_left): t1_h = t1.root.get_height() t2_h = t2.root.get_height() for h in range(t2_h): if add_to_left: needed = t2.root.get_left() # node with the height of t1 at most else: needed = t2.root.get_right() if needed.get_height() <= t1_h: c = needed.get_parent() if add_to_left: x.set_left(t1.root) x.set_right(needed) c.set_left(x) else: x.set_right(t1.root) x.set_left(needed) c.set_right(x) x.set_height(t1_h + 1) self.root = t2.get_root() if self.height_has_changed(c, x): self.Balance(c, x) return"""returns the root of the tree representing the dictionary@rtype: AVLNode@returns: the root, None if the dictionary is empty"""def get_root(self): # return root field , root is updated in the proccess return self.rootdef main(): a=0 for i in range(1, 11): used=set() avl_tree = AVLTree() n = 1000 * pow(2, i) for j in range(n) : num = random.randint(1,n) if num not in used : avl_tree.insert(num, str(num)) used.add(num) print("i is " , i) avl_tree.split(avl_tree.search(n+5))