AVL树

二叉查找树的树高度影响了查找的效率,需要尽量减小树的高度,AVL树正是这样的树。

一、AVL树介绍

Adelson-VelskyLandis

定义如下:

  1. 首先它是一棵二叉查找树。
  2. 任意一个节点的左右子树最大高度差为1。
hh<=1.44log(n)1.44log(n)
hf(h)f(h-1)f(h-2)f(h) = 1 + f(h-1) + f(h-2)f(0)=0,f(1)=1
1.44log(n)1.44log(n)1.44log(n)log(n)
[-1,0,1]

平衡二叉查找树比较难以理解的是添加和删除元素时的调整操作,我们将会具体分析。

二、AVL树基本结构

AVL树的数据结构如下:

// AVL树
type AVLTree struct {
    Root *AVLTreeNode // 树根节点
}

// AVL节点
type AVLTreeNode struct {
    Value  int64                 // 值
    Times  int64                 // 值出现的次数
    Height int64                 // 该节点作为树根节点,树的高度,方便计算平衡因子
    Left   *AVLTreeNode // 左子树
    Right  *AVLTreeNode // 右字树
}

// 初始化一个AVL树
func NewAVLTree() *AVLTree {
    return new(AVLTree)
}
Height

更新树的高度,代码如下:

// 更新节点的树高度
func (node *AVLTreeNode) UpdateHeight() {
    if node == nil {
        return
    }

    var leftHeight, rightHeight int64 = 0, 0
    if node.Left != nil {
        leftHeight = node.Left.Height
    }
    if node.Right != nil {
        rightHeight = node.Right.Height
    }
    // 哪个子树高算哪棵的
    maxHeight := leftHeight
    if rightHeight > maxHeight {
        maxHeight = rightHeight
    }
    // 高度加上自己那一层
    node.Height = maxHeight + 1
}

计算树的平衡因子,也就是左右子树的高度差,代码如下:

// 计算平衡因子
func (node *AVLTreeNode) BalanceFactor() int64 {
    var leftHeight, rightHeight int64 = 0, 0
    if node.Left != nil {
        leftHeight = node.Left.Height
    }
    if node.Right != nil {
        rightHeight = node.Right.Height
    }
    return leftHeight - rightHeight
}

三、AVL树添加元素

添加元素前需要定位到元素的位置,也就是使用二分查找找到该元素需要插入的地方。

[-1,0,1]

旋转有四种情况:

  1. 在右子树上插上右儿子导致失衡,左旋,转一次。
  2. 在左子树上插上左儿子导致失衡,右旋,转一次。
  3. 在左子树上插上右儿子导致失衡,先左后右旋,转两次。
  4. 在右子树上插上左儿子导致失衡,先右后左旋,转两次。

旋转规律记忆法:单旋和双旋,单旋反方向,双旋同方向。

以下示意图摘自维基百科,阅读代码时可以参考。

3.1. 左子树插左儿子:单右旋

在左子树上插上左儿子导致失衡,需要单右旋:

2Root2Root=5
Pivot=35Root5335
2

如果一时难以理解,可以多看几次图好好思考。

代码如下:

// 单右旋操作,看图说话
func RightRotation(Root *AVLTreeNode) *AVLTreeNode {
    // 只有Pivot和B,Root位置变了
    Pivot := Root.Left
    B := Pivot.Right
    Pivot.Right = Root
    Root.Left = B

    // 只有Root和Pivot变化了高度
    Root.UpdateHeight()
    Pivot.UpdateHeight()
    return Pivot
}

3.2. 右子树插右儿子:单左旋

在右子树上插上右儿子导致失衡,需要单左旋:

代码如下:

// 单左旋操作,看图说话
func LeftRotation(Root *AVLTreeNode) *AVLTreeNode {
    // 只有Pivot和B,Root位置变了
    Pivot := Root.Right
    B := Pivot.Left
    Pivot.Left = Root
    Root.Right = B

    // 只有Root和Pivot变化了高度
    Root.UpdateHeight()
    Pivot.UpdateHeight()
    return Pivot
}

3.3. 左子树插右儿子:先左后右旋

在左子树上插上右儿子导致失衡,先左后右旋:

代码如下:

// 先左后右旋操作,看图说话
func LeftRightRotation(node *AVLTreeNode) *AVLTreeNode {
    node.Left = LeftRotation(node.Left)
    return RightRotation(node)
}

直接复用了之前左旋和右旋的代码,虽然难以理解,但是画一下图,确实这样调整后树高度降了,不再失衡,一切 perfect。

3.4. 右子树插左儿子:先右后左旋

在右子树上插上左儿子导致失衡,先右后左旋:

代码如下:

// 先右后左旋操作,看图说话
func RightLeftRotation(node *AVLTreeNode) *AVLTreeNode {
    node.Right = RightRotation(node.Right)
    return LeftRotation(node)
}

3.5. 具体实现

四种旋转代码实现后,我们开始进行添加元素操作:

// 添加元素
func (tree *AVLTree) Add(value int64) {
    // 往树根添加元素,会返回新的树根
    tree.Root = tree.Root.Add(value)
}

func (node *AVLTreeNode) Add(value int64) *AVLTreeNode {
    // 添加值到根节点node,如果node为空,那么让值成为新的根节点,树的高度为1
    if node == nil {
        return &AVLTreeNode{Value: value, Height: 1}
    }
    // 如果值重复,什么都不用做,直接更新次数
    if node.Value == value {
        node.Times = node.Times + 1
        return node
    }

    // 辅助变量
    var newTreeNode *AVLTreeNode

    if value > node.Value {
        // 插入的值大于节点值,要从右子树继续插入
        node.Right = node.Right.Add(value)
        // 平衡因子,插入右子树后,要确保树根左子树的高度不能比右子树低一层。
        factor := node.BalanceFactor()
        // 右子树的高度变高了,导致左子树-右子树的高度从-1变成了-2。
        if factor == -2 {
            if value > node.Right.Value {
                // 表示在右子树上插上右儿子导致失衡,需要单左旋:
                newTreeNode = LeftRotation(node)
            } else {
                //表示在右子树上插上左儿子导致失衡,先右后左旋:
                newTreeNode = RightLeftRotation(node)
            }
        }
    } else {
        // 插入的值小于节点值,要从左子树继续插入
        node.Left = node.Left.Add(value)
        // 平衡因子,插入左子树后,要确保树根左子树的高度不能比右子树高一层。
        factor := node.BalanceFactor()
        // 左子树的高度变高了,导致左子树-右子树的高度从1变成了2。
        if factor == 2 {
            if value < node.Left.Value {
                // 表示在左子树上插上左儿子导致失衡,需要单右旋:
                newTreeNode = RightRotation(node)
            } else {
                //表示在左子树上插上右儿子导致失衡,先左后右旋:
                newTreeNode = LeftRightRotation(node)
            }
        }
    }

    if newTreeNode == nil {
        // 表示什么旋转都没有,根节点没变,直接刷新树高度
        node.UpdateHeight()
        return node
    } else {
        // 旋转了,树根节点变了,需要刷新新的树根高度
        newTreeNode.UpdateHeight()
        return newTreeNode
    }
}
tree.Root = tree.Root.Add(value)
func (node *AVLTreeNode) Add(value int64)
// 添加值到根节点node,如果node为空,那么让值成为新的根节点,树的高度为1
    if node == nil {
        return &AVLTreeNode{Value: value, Height: 1}
    }
Times
// 如果值重复,什么都不用做,直接更新次数
    if node.Value == value {
        node.Times = node.Times + 1
        return node
    }

否则根据值的大小,旋转插入到左子树或右子树,我们只分析插入右子树的代码:

if value > node.Value {
        // 插入的值大于节点值,要从右子树继续插入
        node.Right = node.Right.Add(value)
        // 平衡因子,插入右子树后,要确保树根左子树的高度不能比右子树低一层。
        factor := node.BalanceFactor()
        // 右子树的高度变高了,导致左子树-右子树的高度从-1变成了-2。
        if factor == -2 {
            if value > node.Right.Value {
                // 表示在右子树上插上右儿子导致失衡,需要单左旋:
                newTreeNode = LeftRotation(node)
            } else {
                //表示在右子树上插上左儿子导致失衡,先右后左旋:
                newTreeNode = RightLeftRotation(node)
            }
        }
    }
node.Right = node.Right.Add(value)
factor == -2左子树-右子树-1-2

判断新插入的值是在右子树的左儿子还是右儿子上:

if value > node.Right.Value {
                // 表示在右子树上插上右儿子导致失衡,需要单左旋:
                newTreeNode = LeftRotation(node)
            } else {
                //表示在右子树上插上左儿子导致失衡,先右后左旋:
                newTreeNode = RightLeftRotation(node)
            }
LeftRotation(node)RightLeftRotation(node)

最后需要更新树根节点的高度,并返回树根(如果曾经旋转,表示树根变了,需要返回新的树根):

if newTreeNode == nil {
        // 表示什么旋转都没有,根节点没变,直接刷新树高度
        node.UpdateHeight()
        return node
    } else {
        // 旋转了,树根节点变了,需要刷新新的树根高度
        newTreeNode.UpdateHeight()
        return newTreeNode
    }

3.6. 时间复杂度分析

添加元素时先要找到元素插入的位置,找到位置后逐层自底向上更新每个子树的树高度,并根据子树平衡是否被破坏,需要进行旋转操作。

1.44log(n)1.44log(n)1.44log(n)2.88log(n)log(n)
2次

由于代码的递归实现方式,当某子树旋转过后其父层子树仍然需要判断平衡因子,判断是否需要旋转,该操作是不必要的,因为子树旋转过后全局已经平衡了,不必再判断父层的平衡因子。

func (node *AVLTreeNode) Add(value int64) (newNode *AVLTreeNode, rotate bool)rotate
node.Right, rotate= node.Right.Add(value)
    if !rotate {
        //  子树没有旋转过,那么需要判断是否需要旋转

        // 平衡因子,插入右子树后,要确保树根左子树的高度不能比右子树低一层。
        factor := node.BalanceFactor()
        // 右子树的高度变高了,导致左子树-右子树的高度从-1变成了-2。
        if factor == -2 {
            if value > node.Right.Value {
                // 表示在右子树上插上右儿子导致失衡,需要单左旋:
                newTreeNode = LeftRotation(node)
            } else {
                //表示在右子树上插上左儿子导致失衡,先右后左旋:
                newTreeNode = RightLeftRotation(node)
            }
        }
    }else{
        // do nothing
    }

但此优化意义不大,因为返回辅助变量后仍然需要判断,判断辅助变量和判断平衡因子,时间复杂度一样。

log(n)

四、AVL树查找元素等操作

其他操作与二叉查找树通用,代码如下:

// 找出最小值的节点
func (tree *AVLTree) FindMinValue() *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.FindMinValue()
}

func (node *AVLTreeNode) FindMinValue() *AVLTreeNode {
    // 左子树为空,表面已经是最左的节点了,该值就是最小值
    if node.Left == nil {
        return node
    }

    // 一直左子树递归
    return node.Left.FindMinValue()
}

// 找出最大值的节点
func (tree *AVLTree) FindMaxValue() *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.FindMaxValue()
}

func (node *AVLTreeNode) FindMaxValue() *AVLTreeNode {
    // 右子树为空,表面已经是最右的节点了,该值就是最大值
    if node.Right == nil {
        return node
    }

    // 一直右子树递归
    return node.Right.FindMaxValue()
}

// 查找指定节点
func (tree *AVLTree) Find(value int64) *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.Find(value)
}

func (node *AVLTreeNode) Find(value int64) *AVLTreeNode {
    if value == node.Value {
        // 如果该节点刚刚等于该值,那么返回该节点
        return node
    } else if value < node.Value {
        // 如果查找的值小于节点值,从节点的左子树开始找
        if node.Left == nil {
            // 左子树为空,表示找不到该值了,返回nil
            return nil
        }
        return node.Left.Find(value)
    } else {
        // 如果查找的值大于节点值,从节点的右子树开始找
        if node.Right == nil {
            // 右子树为空,表示找不到该值了,返回nil
            return nil
        }
        return node.Right.Find(value)
    }
}

// 中序遍历
func (tree *AVLTree) MidOrder() {
    tree.Root.MidOrder()
}

func (node *AVLTreeNode) MidOrder() {
    if node == nil {
        return
    }

    // 先打印左子树
    node.Left.MidOrder()

    // 按照次数打印根节点
    for i := 0; i <= int(node.Times); i++ {
        fmt.Println(node.Value)
    }

    // 打印右子树
    node.Right.MidOrder()
}

查找操作逻辑与通用的二叉查找树一样,并无区别。

五、AVL树删除元素

删除元素有四种情况:

  1. 删除的节点是叶子节点,没有儿子,直接删除后看离它最近的父亲节点是否失衡,做调整操作。
  2. 删除的节点下有两个子树,选择高度更高的子树下的节点来替换被删除的节点,如果左子树更高,选择左子树中最大的节点,也就是左子树最右边的叶子节点,如果右子树更高,选择右子树中最小的节点,也就是右子树最左边的叶子节点。最后,删除这个叶子节点,也就是变成情况1。
  3. 删除的节点只有左子树,可以知道左子树其实就只有一个节点,被删除节点本身(假设左子树多于2个节点,那么高度差就等于2了,不符合AVL树定义),将左节点替换被删除的节点,最后删除这个左节点,变成情况1。
  4. 删除的节点只有右子树,可以知道右子树其实就只有一个节点,被删除节点本身(假设右子树多于2个节点,那么高度差就等于2了,不符合AVL树定义),将右节点替换被删除的节点,最后删除这个右节点,变成情况1。
情况1

举个例子,删除叶子节点,如图:

2426

22

实现代码如下:

func (node *AVLTreeNode) Delete(value int64) *AVLTreeNode {
    if node == nil {
        // 如果是空树,直接返回
        return nil
    }
    if value < node.Value {
        // 从左子树开始删除
        node.Left = node.Left.Delete(value)
        // 删除后要更新该子树高度
        node.Left.UpdateHeight()
    } else if value > node.Value {
        // 从右子树开始删除
        node.Right = node.Right.Delete(value)
        // 删除后要更新该子树高度
        node.Right.UpdateHeight()
    } else {
        // 找到该值对应的节点
        // 该节点没有左右子树
        // 第一种情况,删除的节点没有儿子,直接删除即可。
        if node.Left == nil && node.Right == nil {
            return nil // 直接返回nil,表示直接该值删除
        }

        // 该节点有两棵子树,选择更高的哪个来替换
        // 第二种情况,删除的节点下有两个子树,选择高度更高的子树下的节点来替换被删除的节点,如果左子树更高,选择左子树中最大的节点,也就是左子树最右边的叶子节点,如果右子树更高,选择右子树中最小的节点,也就是右子树最左边的叶子节点。最后,删除这个叶子节点。
        if node.Left != nil && node.Right != nil {
            // 左子树更高,拿左子树中最大值的节点替换
            if node.Left.Height > node.Right.Height {
                maxNode := node.Left
                for maxNode.Right != nil {
                    maxNode = maxNode.Right
                }

                // 最大值的节点替换被删除节点
                node.Value = maxNode.Value
                node.Times = maxNode.Times

                // 把最大的节点删掉
                node.Left = node.Left.Delete(maxNode.Value)
                // 删除后要更新该子树高度
                node.Left.UpdateHeight()
            } else {
                // 右子树更高,拿右子树中最小值的节点替换
                minNode := node.Right
                for minNode.Left != nil {
                    minNode = minNode.Left
                }

                // 最小值的节点替换被删除节点
                node.Value = minNode.Value
                node.Times = minNode.Times

                // 把最小的节点删掉
                node.Right = node.Right.Delete(minNode.Value)
                // 删除后要更新该子树高度
                node.Right.UpdateHeight()
            }
        } else {
            // 只有左子树或只有右子树
            // 只有一个子树,该子树也只是一个节点,将该节点替换被删除的节点,然后置子树为空
            if node.Left != nil {
                //第三种情况,删除的节点只有左子树,因为树的特征,可以知道左子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Left.Value
                node.Times = node.Left.Times
                node.Height = 1
                node.Left = nil
            } else if node.Right != nil {
                //第四种情况,删除的节点只有右子树,因为树的特征,可以知道右子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Right.Value
                node.Times = node.Right.Times
                node.Height = 1
                node.Right = nil
            }
        }

        // 找到值后,进行替换删除后,直接返回该节点
        return node
    }

    // 左右子树递归删除节点后需要平衡
    var newNode *AVLTreeNode
    // 相当删除了右子树的节点,左边比右边高了,不平衡
    if node.BalanceFactor() == 2 {
        if node.Left.BalanceFactor() == 1 {
            newNode = RightRotation(node)
        } else {
            newNode = LeftRightRotation(node)
        }
        //  相当删除了左子树的节点,右边比左边高了,不平衡
    } else if node.BalanceFactor() == -2 {
        if node.Right.BalanceFactor() == -1 {
            newNode = LeftRotation(node)
        } else {
            newNode = RightLeftRotation(node)
        }
    }

    if newNode == nil {
        node.UpdateHeight()
        return node
    } else {
        newNode.UpdateHeight()
        return newNode
    }
}

当删除的值不等于当前节点的值时,在相应的子树中递归删除,递归过程中会自底向上维护AVL树的特征。

value < node.Valuenode.Left = node.Left.Delete(value)value > node.Valuenode.Right = node.Right.Delete(value)

因为删除后可能因为旋转调整,导致树根节点变了,这时会返回新的树根,递归删除后需要将返回的新根节点赋予原来的老根节点。

情况1,找到要删除的值时,该值是叶子节点,直接删除该节点即可:

// 第一种情况,删除的节点没有儿子,直接删除即可。
        if node.Left == nil && node.Right == nil {
            return nil // 直接返回nil,表示直接该值删除
        }

情况2,删除的节点有两棵子树,选择高度更高的子树下的节点来替换被删除的节点:

// 该节点有两棵子树,选择更高的哪个来替换
        // 第二种情况,删除的节点下有两个子树,选择高度更高的子树下的节点来替换被删除的节点,如果左子树更高,选择左子树中最大的节点,也就是左子树最右边的叶子节点,如果右子树更高,选择右子树中最小的节点,也就是右子树最左边的叶子节点。最后,删除这个叶子节点。
        if node.Left != nil && node.Right != nil {
            // 左子树更高,拿左子树中最大值的节点替换
            if node.Left.Height > node.Right.Height {
                maxNode := node.Left
                for maxNode.Right != nil {
                    maxNode = maxNode.Right
                }

                // 最大值的节点替换被删除节点
                node.Value = maxNode.Value
                node.Times = maxNode.Times

                // 把最大的节点删掉
                node.Left = node.Left.Delete(maxNode.Value)
                // 删除后要更新该子树高度
                node.Left.UpdateHeight()
            } else {
                // 右子树更高,拿右子树中最小值的节点替换
                minNode := node.Right
                for minNode.Left != nil {
                    minNode = minNode.Left
                }

                // 最小值的节点替换被删除节点
                node.Value = minNode.Value
                node.Times = minNode.Times

                // 把最小的节点删掉
                node.Right = node.Right.Delete(minNode.Value)
                // 删除后要更新该子树高度
                node.Right.UpdateHeight()
            }
        }

情况3和情况4,如果被删除的节点只有一个子树,那么该子树一定没有儿子,不然树的高度就大于1了,所以直接替换值后删除该子树节点:

// 只有左子树或只有右子树
            // 只有一个子树,该子树也只是一个节点,将该节点替换被删除的节点,然后置子树为空
            if node.Left != nil {
                //第三种情况,删除的节点只有左子树,因为树的特征,可以知道左子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Left.Value
                node.Times = node.Left.Times
                node.Height = 1
                node.Left = nil
            } else if node.Right != nil {
                //第四种情况,删除的节点只有右子树,因为树的特征,可以知道右子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Right.Value
                node.Times = node.Right.Times
                node.Height = 1
                node.Right = nil
            }

核心在于删除后的旋转调整,如果删除的值不匹配当前节点的值,对当前节点的左右子树进行递归删除,递归删除后该节点为根节点的子树可能不平衡,我们需要判断后决定要不要旋转这棵树。

每次递归都是自底向上,从很小的子树到很大的子树,如果自底向上每棵子树都进行调整,约束在树的高度差不超过1,那么整棵树自然也符合AVL树的平衡规则。

删除元素后,如果子树失衡,需要进行调整操作,主要有两种:删除后左子树比右子树高,删除后右子树比左子树高。

5.1. 删除后,左子树比右子树高

如果删除了右子树的节点,左边比右边高了,不平衡了:

// 相当删除了右子树的节点,左边比右边高了,不平衡
    if node.BalanceFactor() == 2 {
        if node.Left.BalanceFactor() == 1 {
            newNode = RightRotation(node)
        } else {
            newNode = LeftRightRotation(node)
        }
    }

为什么要这么调整呢,看图说话,有两幅图参考:

这幅图可以看到:

黄色点5.BalanceFactor() == 2node.BalanceFactor() == 2绿色点3.BalanceFactor() == 1node.Left.BalanceFactor() == 1
newNode = RightRotation(node)

这幅图可以看到:

黄色点5.BalanceFactor() == 2node.BalanceFactor() == 2绿色点3.BalanceFactor() == -1node.Left.BalanceFactor() == -1
newNode = LeftRightRotation(node)

还有一种特殊情况,和上面的都不一样,如图:

222320
202node.BalanceFactor() == 213node.BalanceFactor() == 0
else

如果是先左旋后右旋,那么旋转后恢复平衡,如图对根结点进行旋转:

如果使用右旋也可以,如图对根结点进行旋转:

5.2. 删除后,右子树比左子树高

如果删除了左子树的节点,右边比左边高了,不平衡了:

//  相当删除了左子树的节点,右边比左边高了,不平衡
        if node.BalanceFactor() == -2 {
            if node.Right.BalanceFactor() == -1 {
            newNode = LeftRotation(node)
            } else {
            newNode = RightLeftRotation(node)
            }
        }

为什么要这么调整呢,看图说话,有两幅图参考:

这幅图可以看到:

绿色点3.BalanceFactor() == -2node.BalanceFactor() == -2黄色点5.BalanceFactor() == -1node.Left.BalanceFactor() == -1
newNode = LeftRotation(node)

这幅图可以看到:

绿色点3.BalanceFactor() == -2node.BalanceFactor() == -2黄色点5.BalanceFactor() == 1node.Left.BalanceFactor() == 1
newNode = RightLeftRotation(node)
5.1

5.3. 删除后,调整树高度

进行调整操作后,需要更新该子树的高度。如果没有旋转过,更新之前节点的树高度。如果曾经旋转过,树根变了,更新新的树根节点高度。

if newNode == nil {
        node.UpdateHeight()
        return node
    } else {
        newNode.UpdateHeight()
        return newNode
    }

5.4. 时间复杂度分析

删除操作是先找到删除的节点,然后将该节点与一个叶子节点交换,接着删除叶子节点,最后对叶子节点的父层逐层向上旋转调整。

删除操作的时间复杂度和添加操作一样。区别在于,添加操作最多旋转两次就可以达到树的平衡,而删除操作可能会旋转超过两次。

如图是一棵比较糟糕的 AVL 树:

删除节点1,旋转可以一直旋转到根节点,比插入旋转最多旋转两次的次数更多。

六、验证是否是一棵AVL树

如何确保我们的代码实现的就是一棵 AVL 树呢,可以进行验证:

// 验证是不是棵AVL树
func (tree *AVLTree) IsAVLTree() bool {
    if tree == nil || tree.Root == nil {
        return true
    }

    // 判断节点是否符合 AVL 树的定义
    if tree.Root.IsRight() {
        return true
    }

    return false
}

// 判断节点是否符合 AVL 树的定义
func (node *AVLTreeNode) IsRight() bool {
    if node == nil {
        return true
    }

    // 左右子树都为空,那么是叶子节点
    if node.Left == nil && node.Right == nil {
        // 叶子节点高度应该为1
        if node.Height == 1 {
            return true
        } else {
            fmt.Println("leaf node height is ", node.Height)
            return false
        }
    } else if node.Left != nil && node.Right != nil {
        // 左右子树都是满的
        // 左儿子必须比父亲小,右儿子必须比父亲大
        if node.Left.Value < node.Value && node.Right.Value > node.Value {
        } else {
            // 不符合 AVL 树定义
            fmt.Printf("father is %v lchild is %v, rchild is %v\n", node.Value, node.Left.Value, node.Right.Value)
            return false
        }

        bal := node.Left.Height - node.Right.Height
        if bal < 0 {
            bal = -bal
        }

        // 子树高度差不能大于1
        if bal > 1 {
            fmt.Println("sub tree height bal is ", bal)
            return false
        }

        // 如果左子树比右子树高,那么父亲的高度等于左子树+1
        if node.Left.Height > node.Right.Height {
            if node.Height == node.Left.Height+1 {
            } else {
                fmt.Printf("%#v height:%v,left sub tree height: %v,right sub tree height:%v\n", node, node.Height, node.Left.Height, node.Right.Height)
                return false
            }
        } else {
            // 如果右子树比左子树高,那么父亲的高度等于右子树+1
            if node.Height == node.Right.Height+1 {
            } else {
                fmt.Printf("%#v height:%v,left sub tree height: %v,right sub tree height:%v\n", node, node.Height, node.Left.Height, node.Right.Height)
                return false
            }
        }

        // 递归判断子树
        if !node.Left.IsRight() {
            return false
        }

        // 递归判断子树
        if !node.Right.IsRight() {
            return false
        }

    } else {
        // 只存在一棵子树
        if node.Right != nil {
            // 子树高度只能是1
            if node.Right.Height == 1 && node.Right.Left == nil && node.Right.Right == nil {
                if node.Right.Value > node.Value {
                    // 右节点必须比父亲大
                } else {
                    fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                    return false
                }
            } else {
                fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                return false
            }
        } else {
            if node.Left.Height == 1 && node.Left.Left == nil && node.Left.Right == nil {
                if node.Left.Value < node.Value {
                    // 左节点必须比父亲小
                } else {
                    fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                    return false
                }
            } else {
                fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                return false
            }
        }
    }

    return true
}

运行请看完整代码。

七、AVL树完整代码

package main

import (
    "fmt"
)

// AVL树
type AVLTree struct {
    Root *AVLTreeNode // 树根节点
}

// AVL节点
type AVLTreeNode struct {
    Value  int64        // 值
    Times  int64        // 值出现的次数
    Height int64        // 该节点作为树根节点,树的高度,方便计算平衡因子
    Left   *AVLTreeNode // 左子树
    Right  *AVLTreeNode // 右字树
}

// 初始化一个AVL树
func NewAVLTree() *AVLTree {
    return new(AVLTree)
}

// 更新节点的树高度
func (node *AVLTreeNode) UpdateHeight() {
    if node == nil {
        return
    }

    var leftHeight, rightHeight int64 = 0, 0
    if node.Left != nil {
        leftHeight = node.Left.Height
    }
    if node.Right != nil {
        rightHeight = node.Right.Height
    }
    // 哪个子树高算哪棵的
    maxHeight := leftHeight
    if rightHeight > maxHeight {
        maxHeight = rightHeight
    }
    // 高度加上自己那一层
    node.Height = maxHeight + 1
}

// 计算平衡因子
func (node *AVLTreeNode) BalanceFactor() int64 {
    var leftHeight, rightHeight int64 = 0, 0
    if node.Left != nil {
        leftHeight = node.Left.Height
    }
    if node.Right != nil {
        rightHeight = node.Right.Height
    }
    return leftHeight - rightHeight
}

// 单右旋操作,看图说话
func RightRotation(Root *AVLTreeNode) *AVLTreeNode {
    // 只有Pivot和B,Root位置变了
    Pivot := Root.Left
    B := Pivot.Right
    Pivot.Right = Root
    Root.Left = B

    // 只有Root和Pivot变化了高度
    Root.UpdateHeight()
    Pivot.UpdateHeight()
    return Pivot
}

// 单左旋操作,看图说话
func LeftRotation(Root *AVLTreeNode) *AVLTreeNode {
    // 只有Pivot和B,Root位置变了
    Pivot := Root.Right
    B := Pivot.Left
    Pivot.Left = Root
    Root.Right = B

    // 只有Root和Pivot变化了高度
    Root.UpdateHeight()
    Pivot.UpdateHeight()
    return Pivot
}

// 先左后右旋操作,看图说话
func LeftRightRotation(node *AVLTreeNode) *AVLTreeNode {
    node.Left = LeftRotation(node.Left)
    return RightRotation(node)
}

// 先右后左旋操作,看图说话
func RightLeftRotation(node *AVLTreeNode) *AVLTreeNode {
    node.Right = RightRotation(node.Right)
    return LeftRotation(node)
}

// 添加元素
func (tree *AVLTree) Add(value int64) {
    // 往树根添加元素,会返回新的树根
    tree.Root = tree.Root.Add(value)
}

func (node *AVLTreeNode) Add(value int64) *AVLTreeNode {
    // 添加值到根节点node,如果node为空,那么让值成为新的根节点,树的高度为1
    if node == nil {
        return &AVLTreeNode{Value: value, Height: 1}
    }

    // 如果值重复,什么都不用做,直接更新次数
    if node.Value == value {
        node.Times = node.Times + 1
        return node
    }

    // 辅助变量
    var newTreeNode *AVLTreeNode

    if value > node.Value {
        // 插入的值大于节点值,要从右子树继续插入
        node.Right = node.Right.Add(value)
        // 平衡因子,插入右子树后,要确保树根左子树的高度不能比右子树低一层。
        factor := node.BalanceFactor()
        // 右子树的高度变高了,导致左子树-右子树的高度从-1变成了-2。
        if factor == -2 {
            if value > node.Right.Value {
                // 表示在右子树上插上右儿子导致失衡,需要单左旋:
                newTreeNode = LeftRotation(node)
            } else {
                //表示在右子树上插上左儿子导致失衡,先右后左旋:
                newTreeNode = RightLeftRotation(node)
            }
        }
    } else {
        // 插入的值小于节点值,要从左子树继续插入
        node.Left = node.Left.Add(value)
        // 平衡因子,插入左子树后,要确保树根左子树的高度不能比右子树高一层。
        factor := node.BalanceFactor()
        // 左子树的高度变高了,导致左子树-右子树的高度从1变成了2。
        if factor == 2 {
            if value < node.Left.Value {
                // 表示在左子树上插上左儿子导致失衡,需要单右旋:
                newTreeNode = RightRotation(node)
            } else {
                //表示在左子树上插上右儿子导致失衡,先左后右旋:
                newTreeNode = LeftRightRotation(node)
            }
        }
    }

    if newTreeNode == nil {
        // 表示什么旋转都没有,根节点没变,直接刷新树高度
        node.UpdateHeight()
        return node
    } else {
        // 旋转了,树根节点变了,需要刷新新的树根高度
        newTreeNode.UpdateHeight()
        return newTreeNode
    }
}

// 找出最小值的节点
func (tree *AVLTree) FindMinValue() *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.FindMinValue()
}

func (node *AVLTreeNode) FindMinValue() *AVLTreeNode {
    // 左子树为空,表面已经是最左的节点了,该值就是最小值
    if node.Left == nil {
        return node
    }

    // 一直左子树递归
    return node.Left.FindMinValue()
}

// 找出最大值的节点
func (tree *AVLTree) FindMaxValue() *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.FindMaxValue()
}

func (node *AVLTreeNode) FindMaxValue() *AVLTreeNode {
    // 右子树为空,表面已经是最右的节点了,该值就是最大值
    if node.Right == nil {
        return node
    }

    // 一直右子树递归
    return node.Right.FindMaxValue()
}

// 查找指定节点
func (tree *AVLTree) Find(value int64) *AVLTreeNode {
    if tree.Root == nil {
        // 如果是空树,返回空
        return nil
    }

    return tree.Root.Find(value)
}

func (node *AVLTreeNode) Find(value int64) *AVLTreeNode {
    if value == node.Value {
        // 如果该节点刚刚等于该值,那么返回该节点
        return node
    } else if value < node.Value {
        // 如果查找的值小于节点值,从节点的左子树开始找
        if node.Left == nil {
            // 左子树为空,表示找不到该值了,返回nil
            return nil
        }
        return node.Left.Find(value)
    } else {
        // 如果查找的值大于节点值,从节点的右子树开始找
        if node.Right == nil {
            // 右子树为空,表示找不到该值了,返回nil
            return nil
        }
        return node.Right.Find(value)
    }
}

// 删除指定的元素
func (tree *AVLTree) Delete(value int64) {
    if tree.Root == nil {
        // 如果是空树,直接返回
        return
    }

    tree.Root = tree.Root.Delete(value)
}

func (node *AVLTreeNode) Delete(value int64) *AVLTreeNode {
    if node == nil {
        // 如果是空树,直接返回
        return nil
    }
    if value < node.Value {
        // 从左子树开始删除
        node.Left = node.Left.Delete(value)
        // 删除后要更新该子树高度
        node.Left.UpdateHeight()
    } else if value > node.Value {
        // 从右子树开始删除
        node.Right = node.Right.Delete(value)
        // 删除后要更新该子树高度
        node.Right.UpdateHeight()
    } else {
        // 找到该值对应的节点
        // 该节点没有左右子树
        // 第一种情况,删除的节点没有儿子,直接删除即可。
        if node.Left == nil && node.Right == nil {
            return nil // 直接返回nil,表示直接该值删除
        }

        // 该节点有两棵子树,选择更高的哪个来替换
        // 第二种情况,删除的节点下有两个子树,选择高度更高的子树下的节点来替换被删除的节点,如果左子树更高,选择左子树中最大的节点,也就是左子树最右边的叶子节点,如果右子树更高,选择右子树中最小的节点,也就是右子树最左边的叶子节点。最后,删除这个叶子节点。
        if node.Left != nil && node.Right != nil {
            // 左子树更高,拿左子树中最大值的节点替换
            if node.Left.Height > node.Right.Height {
                maxNode := node.Left
                for maxNode.Right != nil {
                    maxNode = maxNode.Right
                }

                // 最大值的节点替换被删除节点
                node.Value = maxNode.Value
                node.Times = maxNode.Times

                // 把最大的节点删掉
                node.Left = node.Left.Delete(maxNode.Value)
                // 删除后要更新该子树高度
                node.Left.UpdateHeight()
            } else {
                // 右子树更高,拿右子树中最小值的节点替换
                minNode := node.Right
                for minNode.Left != nil {
                    minNode = minNode.Left
                }

                // 最小值的节点替换被删除节点
                node.Value = minNode.Value
                node.Times = minNode.Times

                // 把最小的节点删掉
                node.Right = node.Right.Delete(minNode.Value)
                // 删除后要更新该子树高度
                node.Right.UpdateHeight()
            }
        } else {
            // 只有左子树或只有右子树
            // 只有一个子树,该子树也只是一个节点,将该节点替换被删除的节点,然后置子树为空
            if node.Left != nil {
                //第三种情况,删除的节点只有左子树,因为树的特征,可以知道左子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Left.Value
                node.Times = node.Left.Times
                node.Height = 1
                node.Left = nil
            } else if node.Right != nil {
                //第四种情况,删除的节点只有右子树,因为树的特征,可以知道右子树其实就只有一个节点,它本身,否则高度差就等于2了。
                node.Value = node.Right.Value
                node.Times = node.Right.Times
                node.Height = 1
                node.Right = nil
            }
        }

        // 找到值后,进行替换删除后,直接返回该节点
        return node
    }

    // 左右子树递归删除节点后需要平衡
    var newNode *AVLTreeNode
    // 相当删除了右子树的节点,左边比右边高了,不平衡
    if node.BalanceFactor() == 2 {
        if node.Left.BalanceFactor() == 1 {
            newNode = RightRotation(node)
        } else {
            newNode = LeftRightRotation(node)
        }
        //  相当删除了左子树的节点,右边比左边高了,不平衡
    } else if node.BalanceFactor() == -2 {
        if node.Right.BalanceFactor() == -1 {
            newNode = LeftRotation(node)
        } else {
            newNode = RightLeftRotation(node)
        }
    }

    if newNode == nil {
        node.UpdateHeight()
        return node
    } else {
        newNode.UpdateHeight()
        return newNode
    }
}

// 中序遍历
func (tree *AVLTree) MidOrder() {
    tree.Root.MidOrder()
}

func (node *AVLTreeNode) MidOrder() {
    if node == nil {
        return
    }

    // 先打印左子树
    node.Left.MidOrder()

    // 按照次数打印根节点
    for i := 0; i <= int(node.Times); i++ {
        fmt.Println("value:", node.Value, " tree height:", node.BalanceFactor())
    }

    // 打印右子树
    node.Right.MidOrder()
}

// 验证是不是棵AVL树
func (tree *AVLTree) IsAVLTree() bool {
    if tree == nil || tree.Root == nil {
        return true
    }

    // 判断节点是否符合 AVL 树的定义
    if tree.Root.IsRight() {
        return true
    }

    return false
}

// 判断节点是否符合 AVL 树的定义
func (node *AVLTreeNode) IsRight() bool {
    if node == nil {
        return true
    }

    // 左右子树都为空,那么是叶子节点
    if node.Left == nil && node.Right == nil {
        // 叶子节点高度应该为1
        if node.Height == 1 {
            return true
        } else {
            fmt.Println("leaf node height is ", node.Height)
            return false
        }
    } else if node.Left != nil && node.Right != nil {
        // 左右子树都是满的
        // 左儿子必须比父亲小,右儿子必须比父亲大
        if node.Left.Value < node.Value && node.Right.Value > node.Value {
        } else {
            // 不符合 AVL 树定义
            fmt.Printf("father is %v lchild is %v, rchild is %v\n", node.Value, node.Left.Value, node.Right.Value)
            return false
        }

        bal := node.Left.Height - node.Right.Height
        if bal < 0 {
            bal = -bal
        }

        // 子树高度差不能大于1
        if bal > 1 {
            fmt.Println("sub tree height bal is ", bal)
            return false
        }

        // 如果左子树比右子树高,那么父亲的高度等于左子树+1
        if node.Left.Height > node.Right.Height {
            if node.Height == node.Left.Height+1 {
            } else {
                fmt.Printf("%#v height:%v,left sub tree height: %v,right sub tree height:%v\n", node, node.Height, node.Left.Height, node.Right.Height)
                return false
            }
        } else {
            // 如果右子树比左子树高,那么父亲的高度等于右子树+1
            if node.Height == node.Right.Height+1 {
            } else {
                fmt.Printf("%#v height:%v,left sub tree height: %v,right sub tree height:%v\n", node, node.Height, node.Left.Height, node.Right.Height)
                return false
            }
        }

        // 递归判断子树
        if !node.Left.IsRight() {
            return false
        }

        // 递归判断子树
        if !node.Right.IsRight() {
            return false
        }

    } else {
        // 只存在一棵子树
        if node.Right != nil {
            // 子树高度只能是1
            if node.Right.Height == 1 && node.Right.Left == nil && node.Right.Right == nil {
                if node.Right.Value > node.Value {
                    // 右节点必须比父亲大
                } else {
                    fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                    return false
                }
            } else {
                fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                return false
            }
        } else {
            if node.Left.Height == 1 && node.Left.Left == nil && node.Left.Right == nil {
                if node.Left.Value < node.Value {
                    // 左节点必须比父亲小
                } else {
                    fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                    return false
                }
            } else {
                fmt.Printf("%v,(%#v,%#v) child", node.Value, node.Right, node.Left)
                return false
            }
        }
    }

    return true
}

func main() {
    values := []int64{2, 3, 7, 10, 10, 10, 10, 23, 9, 102, 109, 111, 112, 113}

    // 初始化二叉查找树并添加元素
    tree := NewAVLTree()
    for _, v := range values {
        tree.Add(v)
    }

    // 找到最大值或最小值的节点
    fmt.Println("find min value:", tree.FindMinValue())
    fmt.Println("find max value:", tree.FindMaxValue())

    // 查找不存在的99
    node := tree.Find(99)
    if node != nil {
        fmt.Println("find it 99!")
    } else {
        fmt.Println("not find it 99!")
    }

    // 查找存在的9
    node = tree.Find(9)
    if node != nil {
        fmt.Println("find it 9!")
    } else {
        fmt.Println("not find it 9!")
    }

    // 删除存在的9后,再查找9
    tree.Delete(9)
    tree.Delete(10)
    tree.Delete(2)
    tree.Delete(3)
    tree.Add(4)
    tree.Add(3)
    tree.Add(10)
    tree.Delete(111)
    node = tree.Find(9)
    if node != nil {
        fmt.Println("find it 9!")
    } else {
        fmt.Println("not find it 9!")
    }

    // 中序遍历,实现排序
    tree.MidOrder()

    if tree.IsAVLTree() {
        fmt.Println("is a avl tree")
    } else {
        fmt.Println("is not avl tree")
    }
}

运行结果:

find min value: &{2 0 1 <nil> <nil>}
find max value: &{113 0 1 <nil> <nil>}
not find it 99!
find it 9!
not find it 9!
value: 3  tree height: 0
value: 4  tree height: 1
value: 7  tree height: 0
value: 10  tree height: 0
value: 23  tree height: 1
value: 102  tree height: 1
value: 109  tree height: 0
value: 112  tree height: 0
value: 113  tree height: 0
is a avl tree

可以看到,它确实是一棵 AVL 树。

PS:我们的程序是递归程序,如果改写为非递归形式,效率和性能会更好,在此就不实现了,理解AVL树添加和删除的总体思路即可。

八、应用场景

windows
系列文章入口

我是陈星星,欢迎阅读我亲自写的 数据结构和算法(Golang实现),文章首发于 阅读更友好的GitBook。