AVL 树

265 阅读3分钟

⒈ AVL 树介绍

   AVL 树是一种自平衡二叉搜索树,任一节点的做右子树的高度差不会超过 1。 AVL 树示例

   AVL 树的名称来源于其发明者 Georgy Adelson-Velsky 和 Landis。

⒉ 为什么需要 AVL 树

   BST 中的大多数操作(插入、删除、搜索、取最大值、取最小值……)的时间复杂度为 O(logn)\mathcal{O}(\log{}n)。但是,在一些极端情况下 BST 会变成一棵倾斜的树,此时,这些操作的时间复杂度就变成了 O(n)\mathcal{O}(n)

   为了保证操作的时间复杂度为 O(logn)\mathcal{O}(\log{n}),我们就必须保证在每次插入、删除节点后,树的高度 h 为 logn\log{n},这就要求每次插入、删除节点后树都能保持平衡。

⒊ AVL 树自平衡如何实现

  ⓵ 实现原理

   AVL 树的每个节点都有一个**平衡因子(balance factor)**来记录该节点左右子树的高度差。

   平衡因子 = 左子树的高度 - 右子树的高度

  由于 AVL 树的任一节点左右子树的高度差不会超过 1,所以平衡因子的值只能是 -1 或 0 或 1。如果平衡因子的值超出了这个范围,说明该节点已经失去平衡,我们需要对该节点以及其子节点做适当的旋转操作来恢复节点的平衡。 平衡因子

  ⓶ 具体实现

   向 AVL 树中插入一个节点 w,然后从 w 开始向上查找,假设 z 是第一个失去平衡的节点,y 是 z 的子节点,x 是 y 的子节点。则会有以下四种情况需要分别进行不同的旋转操作来恢复 z 节点的平衡。

   ⅰ y 是 z 的左子节点,x 是 y 的左子节点 LL    ⅱ y 是 z 的左子节点,x 是 y 的右子节点    ⅲ y 是 z 的右子节点,x 是 y 的右子节点    ⅳ y 是 z 的右子节点,x 是 y 的左子节点

⒋ 插入、删除的 API 实现

  ⓵ 插入

   AVL 树的节点的插入首先遵循 BST 节点插入的规则,在插入节点后会逐级向上检查每个节点的平衡因子,将失去平衡的节点恢复平衡。

class Node:
    def __init__(self, item):
        self.item = item
        self.left = None
        self.right = None
        self.height = 1

    def __repr__(self):
        return str(self.__dict__)

    def __str__(self):
        return str(self.__dict__)


class AVL:
    def insert(self, root, key):
        """
        将指定的 key 插入 AVL,并将结果重新构造成 AVL
        :param root: AVL 根节点
        :param key: 要插入的值
        :return: AVL tree
        """
        # 构造 BST
        if root is None:
            return Node(key)
        elif key < root.item:
            root.left = self.insert(root.left, key)
        else:
            root.right = self.insert(root.right, key)

        # 更新父节点的高度
        root.height = 1 + max(self.get_height(root.left), self.get_height(root.right))

        # 计算父节点的平衡因子
        balance = self.get_balance(root)

        if balance > 1 and key < root.left.item:
            root = self.right_rotate(root)

        if balance < -1 and key > root.right.item:
            root = self.left_rotate(root)

        if balance > 1 and key > root.left.item:
            root.left = self.left_rotate(root.left)
            root = self.right_rotate(root)

        if balance < -1 and key < root.right.item:
            root.right = self.right_rotate(root.right)
            root = self.left_rotate(root)

        return root
        
    @staticmethod
    def get_height(node):
        """
        计算节点的高度
        :param node:
        :return:
        """
        if node is None:
            return 0

        return node.height

    def get_balance(self, node):
        """
        计算节点的平衡因子
        :param node:
        :return:
        """
        if node is None:
            return 0

        return self.get_height(node.left) - self.get_height(node.right)

    def left_rotate(self, node):
        # 节点旋转
        new_root = node.right
        temp_left = new_root.left

        new_root.left = node
        node.right = temp_left

        # 更新旋转后节点的高度信息
        node.height = 1 + max(self.get_height(node.left), self.get_height(node.right))
        new_root.height = 1 + max(self.get_height(new_root.left), self.get_height(new_root.right))

        return new_root

    def right_rotate(self, node):
        # 节点旋转
        new_root = node.left
        temp_right = new_root.right

        new_root.right = node
        node.left = temp_right

        # 更新节点高度信息
        node.height = 1 + max(self.get_height(node.left), self.get_height(node.right))
        new_root.height = 1 + max(self.get_height(new_root.left), self.get_height(new_root.right))

        return new_root

  ⓶ 删除

   AVL 节点的删除实现逻辑与 BST 的删除实现逻辑相同,需要区分被删除的节点没有子节点、有一个子节点、有两个子节点的情况,分别进行处理。在删除节点后需要额外逐级向上检查每个节点的平衡因子,将失去平衡的节点恢复平衡。

    def delete(self, root, key):
        """
        删除节点
        :param root: AVL 根节点
        :param key: 要删除的节点 item 值
        :return: AVL tree
        """
        if root is None:
            return root
        # BST 删除
        if key < root.item:
            root.left = self.delete(root.left, key)
        elif key > root.item:
            root.right = self.delete(root.right, key)
        else:
            if root.left is None:
                temp = root.right
                root = temp
            elif root.right is None:
                temp = root.left
                root = temp
            else:
                successor_parent = root
                successor = root.right
    
                while successor.left is not None:
                    successor_parent = successor
                    successor = successor.left
    
                if successor_parent != root:
                    successor_parent.left = successor.right
                else:
                    successor_parent.right = successor.right
    
                root.item = successor.item
    
        if root is None:
            return root
        # 计算当前父节点高度
        root.height = 1 + max(self.get_height(root.left), self.get_height(root.right))
        # 计算当前父节点平衡因子
        balance = self.get_balance(root)
        # 旋转再平衡
        if balance > 1 and self.get_balance(root.left) >= 0:
            root = self.right_rotate(root)
    
        if balance < -1 and self.get_balance(root.right) < 0:
            root = self.left_rotate(root)
    
        if balance > 1 and self.get_balance(root.left) < 0:
            root.left = self.left_rotate(root.left)
            root = self.right_rotate(root)
    
        if balance < -1 and self.get_balance(root.right) >= 0:
            root.right = self.right_rotate(root.right)
            root = self.left_rotate(root)
    
        return root

以下为测试代码

my_tree = AVL()
root = None

nums = [9, 5, 10, 0, 6, 11, -1, 1, 2]
for num in nums:
    root = my_tree.insert(root, num)

print(root)

root = my_tree.delete(root, 1)
print(root)