def test_complicated(): tree = TreeNode(50) tree.insert(25) tree.insert(30) tree.insert(27) tree.insert(26) tree.insert(60) tree.insert(70) assert tree.balance() == 2 tree = tree.rebalance(tree) assert tree.balance() == 1
def _put(self, key, val, currentNode): if key < currentNode.key : if currentNode.hasLeftChild() : self._put(key, val, currentNode.leftChild) else : currentNode.leftChild = TreeNode(key, val, parent = currentNode) self.updateBalance(currentNode.leftChild) else : if currentNode.hasRightChild() : self._put(key, val, currentNode.rightChild) else : currentNode.rightChild = TreeNode(key, val, parent = currentNode) self.updateBalance(currentNode.rightChild)
def test_insert_and_size(): tree = TreeNode(0) assert tree.size() == 1 tree.insert(1) assert tree.size() == 2 tree.insert(1) assert tree.size() == 2
def test_right_linked_list_four(): tree = TreeNode(0) tree.insert(1) tree.insert(2) tree.insert(3) assert tree.balance() == -3 tree = tree.rebalance(tree) assert tree.balance() == 1
def test_node_exists_args(): """Test Create a Tree Node.""" from bst import TreeNode test_node = TreeNode(1, 2, 3) assert test_node
def test_left_linked_list_four(): tree = TreeNode(3) tree.insert(2) tree.insert(1) tree.insert(0) assert tree.balance() == 3 assert tree.val == 3 tree = tree.rebalance(tree) assert tree.balance() == -1 assert tree.val == 1
def test_left_linked_list(): tree = TreeNode(2) tree.insert(1) tree.insert(0) assert tree.balance() == 2 tree = tree.rebalance(tree) assert tree.balance() == 0 treelist = [n for n in tree.depth_traversal("in")] assert treelist == [0, 1, 2]
def insert(self, value): # insert new node tmp_node = super()._insert(value) # create new TreeNode with red color new_node = TreeNode(value) #red by default parent = tmp_node.get_parent() # take care of node-parent connection if not parent or value == parent.get_data(): return elif value > parent.get_data(): parent.set_right(new_node) elif value < parent.get_data(): parent.set_left(new_node) # recolor starting from new_node till root self.root = self.__recolor(new_node) # root is black (isn't essential tho!!) self.root.set_color(Color.BLACK)
######################### INSERTION ######################### def insert(self, value): _ = super()._insert(value) self.rebalance() ######################### REMOVAL ######################### def remove(self, del_value): _ = super()._remove(del_value) self.rebalance() if __name__ == "__main__": # to test left rotation avl = AVL(22) avl.root.set_right(TreeNode(43)) avl.root.set_left(TreeNode(18)) avl.root.get_left().set_left(TreeNode(9)) avl.root.get_left().set_right(TreeNode(21)) avl.root.get_left().get_left().set_left(TreeNode(6)) avl.root.get_left().get_left().get_left().set_left(TreeNode(0)) print(avl, '\n') avl.rebalance() print(avl) print('=' * 50) ####################################### # to test left-right rotation avl = AVL(78) avl.root.set_right(TreeNode(88)) avl.root.set_left(TreeNode(50))
def __init__(self, value): if isinstance(value, TreeNode): self.root = value else: self.root = TreeNode(value) self.root.set_color(Color.BLACK)
class RedBlackTree(BST): def __init__(self, value): if isinstance(value, TreeNode): self.root = value else: self.root = TreeNode(value) self.root.set_color(Color.BLACK) ############################## HEIGHT ############################## def get_black_height(self): #TODO pass ############################## ROTATION ############################## def __rotate_left(self, start_node): # print("Rotating Left") middle = start_node.get_right() middle.set_parent( start_node.get_parent() ) start_node.set_right(middle.get_left()) middle.set_left(start_node) return middle def __rotate_right(self, start_node): # print("Rotating Right") middle = start_node.get_left() middle.set_parent( start_node.get_parent() ) start_node.set_left(middle.get_right()) middle.set_right(start_node) return middle ############################## INSERTION ############################## def __recolor_case3(self, start_node): # get basic info uncle = start_node.get_uncle() parent = start_node.get_parent() grandparent = parent.get_parent() if parent else None # parent is left-child and start_node is left-child if parent.is_left_child() and start_node.is_left_child(): grandparent.set_color(Color.RED) parent.set_color(Color.BLACK) grandparent = self.__rotate_right(grandparent) # parent is left-child and start_node is right-child elif parent.is_left_child() and not start_node.is_left_child(): # first rotation parent = self.__rotate_left(parent) grandparent.set_left(parent) grandparent.set_color(Color.RED) # second rotation grandparent = self.__rotate_right(grandparent) grandparent.set_color(Color.BLACK) # parent is right-child and start_node is left-child elif not parent.is_left_child() and start_node.is_left_child(): # first rotation parent = self.__rotate_right(parent) grandparent.set_right(parent) grandparent.set_color(Color.RED) # second rotation grandparent = self.__rotate_left(grandparent) grandparent.set_color(Color.BLACK) # parent is right-child and start_node is right-child else: grandparent.set_color(Color.RED) parent.set_color(Color.BLACK) grandparent = self.__rotate_left(grandparent) return grandparent def __recolor(self, start_node): """ Recoloring can be done according to these three cases: - case I: parent is 'black' - case II: parent is 'red' and uncle is 'red' - case III: parent is 'red' and uncle is 'black' """ # get basic info uncle = start_node.get_uncle() parent = start_node.get_parent() grandparent = parent.get_parent() if parent else None # recolor when node has a grandparent if parent is None or grandparent is None: return parent if parent else start_node # case I if parent.get_color() == Color.BLACK: #do nothing # print("Case I") return self.root else: # case II if uncle and uncle.get_color() == Color.RED: # print("Case II") parent.set_color(Color.BLACK) uncle.set_color(Color.BLACK) grandparent.set_color(Color.RED) # case III else: # print("Case III") # get great grandparent great_grandparent = grandparent.get_parent() grandparent = self.__recolor_case3(start_node) # set connection if great_grandparent: if great_grandparent.data > grandparent.get_data(): great_grandparent.set_left(grandparent) else: great_grandparent.set_right(grandparent) # recursively do the same over grandparent return self.__recolor(grandparent) def insert(self, value): # insert new node tmp_node = super()._insert(value) # create new TreeNode with red color new_node = TreeNode(value) #red by default parent = tmp_node.get_parent() # take care of node-parent connection if not parent or value == parent.get_data(): return elif value > parent.get_data(): parent.set_right(new_node) elif value < parent.get_data(): parent.set_left(new_node) # recolor starting from new_node till root self.root = self.__recolor(new_node) # root is black (isn't essential tho!!) self.root.set_color(Color.BLACK) ############################## REMOVAL ############################## def _find_replacement(self, start_node): """ NOTE: Here, we're tyring to exploit two characteristics of Red-black trees and they are: - red-nodes are good replacements. - when removing a red node, there must be at least one red-node as a replacement at least. """ if start_node.is_leaf(): replacement_node = None else: # in-order successor successor = super()._get_min_node(start_node.get_right()) \ if start_node.get_right() else None # in-order predecessor predecessor = super()._get_max_node(start_node.get_left()) \ if start_node.get_left() else None # find the red-node if successor and successor.get_color() == Color.RED: replacement_node = successor elif predecessor and predecessor.get_color() == Color.RED: replacement_node = predecessor else: replacement_node = successor if successor else predecessor return replacement_node def __get_node_and_replacement(self, del_value, start_node): #TODO: try to get rid of this function and use find() or search instead. curr_value = start_node.get_data() # when del_value is found if del_value == curr_value: replacement = self._find_replacement(start_node) return start_node, replacement # search left-side elif del_value < curr_value: if start_node.get_left() is None: # raise ValueError("Couldn't find given value in the tree!!") return start_node, None else: return self.__get_node_and_replacement(del_value, start_node.get_left()) # search right-side else: if start_node.get_right() is None: # raise ValueError("Couldn't find given value in the tree!!") return start_node, None else: return self.__get_node_and_replacement(del_value, start_node.get_right()) def __transplant(self, node, replacement): parent = node.get_parent() if replacement is None: if parent is None: self.__transplant(replacement, None) node.data = replacement.data return parent, node else: if node.is_left_child(): parent.set_left(replacement) return parent, parent.get_left() else: parent.set_right(replacement) return parent, parent.get_right() else: if replacement.is_leaf(): new_replacement = None elif replacement.get_left(): new_replacement = replacement.get_left() else: new_replacement = replacement.get_right() # transplant data & color node.data = replacement.data node.set_color(replacement.get_color()) self.__transplant(replacement, new_replacement) return parent, node def __handle_double_black_case1(self, parent, double_black_node): pass def __handle_double_black_case2(self, parent, double_black_node, sibling): parent.set_color(Color.RED) sibling.set_color(Color.BLACK) if sibling.is_left_child(): parent = self.__rotate_right(parent) else: parent = self.__rotate_left(parent) return parent def __handle_double_black_case3(self, parent, double_black_node, sibling): sibling.set_color(Color.RED) grandparent = parent.get_parent() if def __handle_double_black_case4(self, parent, double_black_node): pass def __handle_double_black_case5(self, parent, double_black_node): pass def __handle_double_black_case6(self, parent, double_black_node): pass def __handle_double_black(self, parent, double_black_node): """ SRC: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree When dealing with double black nodes, we have six cases to consider: Case I : if double_black_node is root Case II : (s) is red Case III: (p) and (s) are black and the two children of (s) are black Case IV : (p) is red, (s) is black and the two children of (s) are black Case V : (s) is black, left-child of (s) is red, right-child of (s) is black and (s) is the right-child Case VI : (s) is black, right-child of (s) is red, and (s) is the right- child Note: (s) is the sibling of the double_black_node and (p) is the parent """ # Case I if parent is None: return double_black_node else: grandparent = parent.get_parent() sibling = double_black_node.get_sibling() \ if double_black_node \ else parent.get_left() \ if parent.get_left() else parent.get_right() print("sibling:", sibling) if sibling is None: pass else: s_left_child = sibling.get_left() s_right_child = sibling.get_right() # get colors of sibling's children s_left_color = s_left_child.get_color() if s_left_child \ else Color.BLACK s_right_color = s_right_child.get_color() if s_right_child \ else Color.BLACK # Case II if sibling.get_color() == Color.RED: parent = self.__handle_double_black_case2(parent, double_black_node) # Case III elif (parent.get_color() == Color.BLACK and sibling.get_color() == Color.BLACK and s_left_color == Color.BLACK and s_right_color == Color.BLACK): def remove(self, del_value): """ Case I : removed_node is 'red', replacement is either 'red' or None Case II : removed_node is 'red', replacement is 'black' Case III: removed_node is 'black', replacement is either 'black' or None Case IV : removed_node is 'black', replacement is 'red' """ removed_node, replacement = self.__get_node_and_replacement(del_value, self.root) print("replacement:", replacement) # couldn't find the del_value in the tree if removed_node.get_data() != del_value: return # Case I (replace red-node with red-node/None) if removed_node.get_color() == Color.RED and \ (replacement is None or replacement.get_color() == Color.RED): print("Case I (replace red-node with red-node/None)") self.__transplant(removed_node, replacement) # Case II (replace red-node with black-node) elif removed_node.get_color() == Color.RED and \ replacement.get_color() == Color.BLACK: print("Case II (replace red-node with black-node)") raise ValueError("This case shouldn't occur!!") # Case III (replace black-node with black-node) elif removed_node.get_color() == Color.BLACK and \ (replacement is None or replacement.get_color() == Color.BLACK): print("Case III (double black-node)") parent, transplanted = self.__transplant(removed_node, replacement) double_black_node = transplanted # handle this double black root = self.__handle_double_black(parent, double_black_node) self.root = root # Case IV (replace black-node with red-node/None) elif removed_node.get_color() == Color.BLACK and \ replacement.get_color() == Color.RED: print("Case IV (replace black-node with black-node/None)") self.__transplant(removed_node, replacement)
def gimme_a_tree(): vals = [3, 4, 1, 0, 7, 9, 8, 6, 10] head = TreeNode(5) for v in vals: head.insert(v) return head
def test_balance(): tree = TreeNode(5) assert tree.balance() == 0 tree.insert(1) assert tree.balance() == 1 tree.insert(6) assert tree.balance() == 0 tree.insert(3) tree.insert(4) assert tree.balance() == 2
def test_contains(): tree = TreeNode(0) assert tree.contains(2) is False tree.insert(2) assert tree.contains(2)
def test_depth(): tree = TreeNode(5) assert tree.depth() == 1 tree.insert(1) assert tree.depth() == 2 tree.insert(6) assert tree.depth() == 2 tree.insert(3) assert tree.depth() == 3
def tree_node(): from bst import TreeNode return TreeNode()
return left elif right: return right return None def findLowestAnsestorBST(root, n1, n2): if root is None: return None else: if root.data > n1 and root.data > n2: findLowestAnsestorBST(root.left, n1, n2) elif root.data < n1 and root.data < n2: findLowestAnsestor(root.right, n1, n2) return root if __name__ == '__main__': root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5) root.right.left = TreeNode(6) root.right.right = TreeNode(7) print "LCA(4, 5) = ", findLowestAnsestor(root, 4, 5) print "LCA(4, 6) = ", findLowestAnsestor(root, 4, 6) print "LCA(3, 4) = ", findLowestAnsestor(root, 3, 4) print "LCA(2, 4) = ", findLowestAnsestor(root, 2, 4)
raise NotImplementedError("You can't check height of Splay Trees!!") def get_depth(self): raise NotImplementedError("You can't check depth of Splay Trees!!") if __name__ == "__main__": # test insert # example from Data Structures and Algorithm in Python (page: 514) stree = SplayTree(8) stree.root.set_left(TreeNode(3)) stree.root.get_left().set_right(TreeNode(4)) stree.root.get_left().get_right().set_right(TreeNode(6)) stree.root.get_left().get_right().get_right().set_left(TreeNode(5)) stree.root.get_left().get_right().get_right().set_right(TreeNode(7)) stree.root.set_right(TreeNode(10)) stree.root.get_right().set_right(TreeNode(11)) stree.root.get_right().get_right().set_right(TreeNode(12)) stree.root.get_right().get_right().get_right().set_right(TreeNode(16)) stree.root.get_right().get_right().get_right().get_right().set_left(TreeNode(13)) stree.root.get_right().get_right().get_right().get_right().set_right(TreeNode(17)) stree.insert(14) stree.find(13) print(stree) stree.find(8) print(stree)