def left_rotate(self, z: BinaryTreeNode) -> BinaryTreeNode: """ Left-rotate subtree around pivot z Parameters: z (BinaryTreeNode): root node of subtree to pivot rotation on Returns: BinaryTreeNode: root of left-rotated subtree """ # get references to child nodes of pivot y = z.right # right child of pivot T2 = y.left # left child of y # rotate nodes around pivot y.left = z # y becomes new root of subtree, and pivot becomes child of y z.right = T2 # T2 becomes right node of pivot # update height of rotated nodes z.height = 1 + max(self.__get_height(z.left), self.__get_height( z.right)) y.height = 1 + max(self.__get_height(y.left), self.__get_height( y.right)) # return the new root return y
def __array_to_bst(self, data: list) -> BinaryTreeNode: """ Converts a sorted array into a height-balanced BST Parameters: data (list): sorted data array Returns: BinaryTreeNode: root node of built BST """ # length of data in current recursion n = len(data) # no input data -> no tree if n == 0: return None # single input data element -> node with value elif n == 1: return BinaryTreeNode(data[0]) # multiple data elements are recursively split by the median else: # root node is the median mid = n // 2 root = BinaryTreeNode(data[mid]) # left and right subtrees are built from left and right partition root.left = self.__array_to_bst(data[:mid]) root.right = self.__array_to_bst(data[mid + 1:]) return root
def __delete(self, root: BinaryTreeNode, key: int) -> BinaryTreeNode: """ Delete value from BST by recursively traversing the tree Parameters: root (BinaryTreeNode): root node of subtree val (int): key to delete Returns: BinaryTreeNode: root of node of subtree """ # if root does not exist, there is no more to traverse if root is None: return root # if key is smaller than root, the key must be in the left subtree if key < root.data: root.left = self.__delete(root.left, key) # if key is greater than root, the key must be in the right subtree elif key > root.data: root.right = self.__delete(root.right, key) # else the key is equal to the root, and the root must be deleted else: # if root has no children it is deleted; if root has only one child it becomes the new root if root.left is None: child_node = root.right root = None return child_node elif root.right is None: child_node = root.left root = None return child_node # if node has two children, the inorder succesor is chosen as new root # inorder-succesor is the smallest in the right subtree temp = self.__get_min_value(root.right) # the value of the new root is copied to the current root root.key = temp.key # the inorder succesor is then deleted root.right = self.__delete(root.right, temp.key) return root
def __invert_tree(self, root: BinaryTreeNode) -> BinaryTreeNode: """ Inverts the tree by recusively inverting the subtrees Parameters: root (BinaryTreeNode): root node of BST Returns: BinaryTreeNode: the root node of the inverted BST """ if root is None: return None else: # recursively invert left subtree left = self.__invert_tree(root.left) # recursively invert right subtree right = self.__invert_tree(root.right) # invert children root.left = right root.right = left return root
def __insert(self, root: BinaryTreeNode, val: int) -> BinaryTreeNode: """ Inserts value into BST by recursively traversing the tree Parameters: root (BinaryTreeNode): root node of subtree val (int): value to insert Returns: BinaryTreeNode: root of node of subtree """ # if no root exists, the new node is the root if root is None: return BinaryTreeNode(val) # if value to insert is smaller than root, traverse down left subtree if val < root.data: root.left = self.__insert(root.left, val) # otherwise, traverse down right subtree else: root.right = self.__insert(root.right, val) return root
stack = [(root, -math.inf, math.inf)] while True: current, lower, upper = stack.pop() if (current.value <= lower) or (current.value >= upper): return False if current.left: stack.append((current.left, lower, current.value)) if current.right: stack.append((current.right, current.value, upper)) if not stack: break return True tree = BinaryTreeNode(6) tree.left = BinaryTreeNode(3) tree.right = BinaryTreeNode(9) tree.left.left = BinaryTreeNode(1) tree.left.right = BinaryTreeNode(7) tree.right.left = BinaryTreeNode(7) tree.right.right = BinaryTreeNode(10) print(valid_bst(tree))
def __delete(self, root: BinaryTreeNode, key: int) -> BinaryTreeNode: """ Delete value from BST by recursively traversing the tree. The tree is balanced by traveling up the tree from a newly inserted node until an unbalance node (z) is encountered. The subtree of the unbalanced node is then balanced by doing one of four possible operations that rotate the node and its child nodes. Selecting the right operation is a matter of finding the height difference between the two subtrees to determine whether they are balanced, and finding the two tallest children of z (meaning the children that have highest subtress themselves) to determine whether they are left-left, left-right, right-right or right-left children. Parameters: root (BinaryTreeNode): root node of subtree val (int): key to delete Returns: BinaryTreeNode: root of node of subtree """ # if root does not exist, there is no more to traverse if root is None: return root # if key is smaller than root, the key must be in the left subtree if key < root.data: root.left = self.__delete(root.left, key) # if key is greater than root, the key must be in the right subtree elif key > root.data: root.right = self.__delete(root.right, key) # else the key is equal to the root, and the root must be deleted else: # if root has no children it is deleted; if root has only one child it becomes the new root if root.left is None: child_node = root.right root = None return child_node elif root.right is None: child_node = root.left root = None return child_node # if node has two children, the inorder succesor is chosen as new root # inorder-succesor is the smallest in the right subtree temp = self.__get_min_value(root.right) # the value of the new root is copied to the current root root.data = temp.data # the inorder succesor is then deleted root.right = self.__delete(root.right, temp.data) # update height attribute of parent node root.height = 1 + max(self.__get_height(root.left), self.__get_height(root.right)) # get 'balance factor' (height difference between left and right subtrees) balance = self.get_height_diff(root) # balance subtree if unbalanced # Case 1: left-left rotation # z is the unbalance node, y is the taller child of z, x is the taller child of y # - z - # | | # - y - T4 # | | # - x - T3 # | | # T1 T2 # balance factor is greater than 1, while x and y are both left children if balance > 1 and self.get_height_diff(root.left) >= 0: return self.right_rotate(root) # Case 2: left-right rotation # z is the unbalance node, y is the taller child of z, x is the taller child of y # - z - # | | # - y - T4 # | | # T1 - x - # | | # T1 T2 # balance factor is greater than 1, while y is a left child and x is a right child if balance > 1 and self.get_height_diff(root.left) < 0: root.left = self.left_rotate(root.left) return self.right_rotate(root) # Case 3: right-right rotation # z is the unbalance node, y is the taller child of z, x is the taller child of y # - z - # | | # T1 - y - # | | # T2 - x - # | | # T3 T4 # balance factor is smaller than 1, while x and y are both right children if balance < -1 and self.get_height_diff(root.right) <= 0: return self.left_rotate(root) # Case 4: right-left rotation # z is the unbalance node, y is the taller child of z, x is the taller child of y # - z - # | | # T1 - y - # | | # - x - T4 # | | # T2 T3 # balance factor is smaller than 1, while y is a right child and x is a left child if balance < -1 and self.get_height_diff(root.right) > 0: root.right = self.right_rotate(root.right) return self.left_rotate(root) return root
def __insert(self, root: BinaryTreeNode, data: int): """ Inserts a node into the AVL tree while ensuring the tree remains balanced. The tree is balanced by traveling up the tree from a newly inserted node until an unbalance node (z) is encountered. The subtree of the unbalanced node is then balanced by doing one of four possible operations that rotate the node and its child nodes. Selecting the right operation is a matter of finding the height difference between the two subtrees to determine whether they are balanced, and comparing the values of the last two children (x and y) along the traveled path to determine whether they are left-left, left-right, right-right or right-left children. Parameters: root (BinaryTreeNode): root node of subtree data (int): data value of node to be inserted """ # insert new node into tree as in a traditional BST if not root: return BinaryTreeNode(data) elif data < root.data: root.left = self.__insert(root.left, data) else: root.right = self.__insert(root.right, data) # update height attribute of parent node root.height = 1 + max(self.__get_height(root.left), self.__get_height(root.right)) # get 'balance factor' (height difference between left and right subtrees) balance = self.get_height_diff(root) # balance subtree if unbalanced # Case 1: left-left rotation # z is the unbalance node, y is the first child, and x is the grandchild # - z - # | | # - y - T4 # | | # - x - T3 # | | # T1 T2 # balance factor is greater than 1, while x and y are both left children if balance > 1 and data < root.left.data: return self.right_rotate(root) # Case 2: left-right rotation # z is the unbalance node, y is the first child, and x is the grandchild # - z - # | | # - y - T4 # | | # T1 - x - # | | # T1 T2 # balance factor is greater than 1, while y is a left child and x is a right child if balance > 1 and data > root.left.data: root.left = self.left_rotate(root.left) return self.right_rotate(root) # Case 3: right-right rotation # z is the unbalance node, y is the first child, and x is the grandchild # - z - # | | # T1 - y - # | | # T2 - x - # | | # T3 T4 # balance factor is greater than 1, while x and y are both right children if balance < -1 and data > root.right.data: return self.left_rotate(root) # Case 4: right-left rotation # z is the unbalance node, y is the first child, and x is the grandchild # - z - # | | # T1 - y - # | | # - x - T4 # | | # T2 T3 # balance factor is smaller than 1, while y is a right child and x is a left child if balance < -1 and data < root.right.data: root.right = self.right_rotate(root.right) return self.left_rotate(root) return root