Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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