avl树

作者: zuixinan | 来源:发表于2020-02-26 11:28 被阅读0次

概述

BST(二叉搜索树)可以提高查找效率,理想的情况下BST的每个子树的高度差相等(BST平衡),搜索的时间复杂度为O(lgn)。如果数据有序或接近会导致生成BTS中出现线性结构(BST失衡),搜索的时间复杂度会变为O(n)。

AVL(平衡二叉树)是BST的一个进化体, 得名于其发明者的名字( Adelson-Velskii 以及 Landis)。对于AVL树的每一个节点,它的左右子树的高度之差不能超过1,如果插入或者删除操作使得高度之差大于1,就要进行节点之间的旋转,将二叉树重新维持在一个平衡状态。

avl树特点

  • AVL树是一棵二叉搜索树。

  • AVL树的左右子节点也是AVL树。

  • 每个节点的左右子节点的高度之差(简称平衡因子)的绝对值不超过1(故一个结点的平衡因子可以是-1/0/1)。

具体实现

AVL树插入或删除一个节点可能会导致AVL树平衡因子的绝对值大于1,此时需要进行旋转操作以实现重新平衡。

旋转

旋转两种情况,分别是左旋和右旋。以右旋操作为例, 节点B上升同时节点A下降为节点B的右子节点,原节点B的右子节点D变为节点A的左子节点。左旋操作同理。

image-20200214222026784.png

失衡

实际情况下无外乎如下图所示4种失衡状态。


WeChat9cea3c866502213c05506d647591a5ee.png

重平衡

这四种失衡状态都可以通过一次或两次旋转达到重平衡状态。其中 case1和case2通过一次旋转即可重新平衡,case3和case4需要做两次旋转达到平衡。

  • case3 先对y节点进行左旋转, 再对z节点进行右旋转
  • case4 先对z节点进行右旋转,再对x节点进行左旋转。
image-20200214220940353.png

代码实现

结构定义

为了计算平衡因子,需要在节点增加一个高度属性, 用于存储左右子树高度的最大值。

type Node struct {
    Left   *Node
    Right  *Node
    Height int
    Key    int
    Val    interface{}
}

平衡因子计算

节点的平衡因子为左右子树的高度差

//获取节点高度
func (n *Node) GetHeight() int {

    if n == nil {
        return 0
    }

    return n.Height
}

//节点高度差
func (n *Node) GetBalanceFactor() int {

    if n == nil {
        return -1
    }

    return n.Left.GetHeight() - n.Right.GetHeight()
}

节点旋转

//左旋转
func (n *Node) LeftRotation() *Node {

    root, newRoot := n, n.Right

    //旋转
    root.Right = newRoot.Left
    newRoot.Left = root

    //更新高度
    root.Height = Max(root.Left.GetHeight(), root.Right.GetHeight()) + 1
    newRoot.Height = Max(newRoot.Left.GetHeight(), newRoot.Right.GetHeight()) + 1

    return newRoot
}

//右旋转
func (n *Node) RightRotation() *Node {

    root, newRoot := n, n.Left

    //旋转
    root.Left = newRoot.Right
    newRoot.Right = root

    //更新高度
    root.Height = Max(root.Left.GetHeight(), root.Right.GetHeight()) + 1
    newRoot.Height = Max(newRoot.Left.GetHeight(), newRoot.Right.GetHeight()) + 1

    return newRoot
}

节点插入

当需要插入一个新结点时,从根节点开始递归查找,直到找到或nil, 执行更新或插入操作。节点插入完成后,依次检查所有父节点是否失衡。

代码中 balance 为 n.left.height - n.right.height, 如果balance的绝对值大于1,则当前节点失衡。当balance > 1时,则说明插入节点在左侧,反之在右侧。如果插入key小于节点key, 则说明插入节点在左侧,反之在右侧。由此可以判断出是否存在四种失衡情况。

//插入
func (n *Node) Insert(key int, val interface{}) *Node {

    if n == nil {
        return &Node{
            Height: 1,
            Key:    key,
            Val:    val,
        }
    }

    if key < n.Key {
        n.Left = n.Left.Insert(key, val)
    } else if key > n.Key {
        n.Right = n.Right.Insert(key, val)
    } else {
        n.Val = val
        return n
    }

    //更新高度
    n.Height = Max(n.Left.GetHeight(), n.Right.GetHeight()) + 1
    balance := n.GetBalanceFactor()

    //case1
    if balance > 1 && key < n.Left.Key {
        return n.RightRotation()
    }

    //case2
    if balance < -1 && key > n.Right.Key {
        return n.LeftRotation()
    }

    //case3
    if balance > 1 && key > n.Left.Key {
        n.Left = n.Left.LeftRotation()
        return n.RightRotation()
    }

    //case4
    if balance < -1 && key < n.Right.Key {
        n.Right = n.Right.RightRotation()
        return n.LeftRotation()
    }

    return n
}

节点删除

节点删除步骤:

  1. 递归查找要删除的节点, 如未找到则不处理。

  2. 删除节点是否存在子节点。

    • 若不存在子节点直接删除。

    • 若存在一个子节点, 删除节点后子节点接上

    • 若存在两个子节点, 查找右子树中最小子节点替换掉待删除节点,删除右子树中最小子节点。

  3. 判断时候失衡,失衡则重平衡。

//删除
func (n *Node) Delete(key int) *Node {

    root := n
    if root == nil {
        return nil
    }

    if key < root.Key {
        root.Left = root.Left.Delete(key)
    } else if key > root.Key {
        root.Right = root.Right.Delete(key)
    } else {

        //存在 0个或1个子节点
        if root.Left == nil || root.Right == nil {

            //没做子节点
            if root.Left == nil && root.Right == nil {
                return nil
            }

            if root.Left != nil {
                root = root.Left
            } else {
                root = root.Right
            }
        } else {

            //查找最小子节点
            minNode := root.Right.SearchMinNode()

            //替换当前节点
            root.Key = minNode.Key
            root.Val = minNode.Val

            //删除替换节点
            root.Right = root.Right.Delete(minNode.Key)
        }
    }

    root.Height = Max(root.Left.GetHeight(), root.Right.GetHeight()) + 1

    balance := root.GetBalanceFactor()
    if balance > 1 {

        leftBalance := root.Left.GetBalanceFactor()
        if leftBalance >= 0 {
            return root.RightRotation()
        } else {
            root.Left = root.Left.LeftRotation()
            return root.RightRotation()
        }
    } else if balance < -1 {

        rightBalance := root.Right.GetBalanceFactor()
        if rightBalance <= 0 {
            return root.LeftRotation()
        } else {
            root.Right = root.Right.RightRotation()
            return root.LeftRotation()
        }
    }

    return root
}

//查找最小节点
func (n *Node) SearchMinNode() *Node {

    cur := n
    for cur.Left != nil {
        cur = cur.Left
    }

    return cur
}

参考链接

相关文章

网友评论

      本文标题:avl树

      本文链接:https://www.haomeiwen.com/subject/nmcmchtx.html