Contents
  1. 1. 平衡还是不平衡,这是一个问题
  2. 2. 什么是二叉查找树?
    1. 2.1. 为什么说平衡时相等?
  3. 3. 如何达到自平衡的效果?
    1. 3.1. 什么是旋转?
  4. 4. 如何实现?
    1. 4.1. 节点 - Node
    2. 4.2. 递归计算节点的高度
    3. 4.3. 计算平衡因子
    4. 4.4. 右旋
    5. 4.5. 左旋
  5. 5. 搞懂了吗? 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️

平衡还是不平衡,这是一个问题

😳:平衡好不好呢?
🤣:我说它好。
😳:好在哪里呢?
🤣:我只能告诉你无可奉告。

以上的对话是大多数人的反应,对于数据结构中的树形结构为什么平衡会比较好不求甚解。只是大概会比不平衡好吧,毕竟万物都是追求和谐的。

树形结构的平衡好不好?当然好了,这就是产生树的基本思想啊,因为极端的不平衡就是线性表啊。

所谓树形结构的平衡性,就是指尽量使树中的节点的子树的深度都大致相同,最理想的情况就是类似于完全二叉树那样的形状。
在这里插入图片描述
平衡带来的好处有什么呢?
🤣操作时间复杂度低,诸如查询,插入,删除等等curd
🤣好像没了,但是数据结构不就是最在意这个吗


什么是二叉查找树?

这一节大概讲的都是废话。。一般来看这个文章的应该都知道二叉查找树是什么吧。。

Binary Search Tree,BST就是二叉查找树,遵循最基本的“查找树”的性质:

  • 对于树中的任意一个节点:
    • 如果这个节点的左子树不为空,则左子树上的所有节点都小于该节点的值
    • 如果这个节点的右子树不为空,则右子树上的所有节点都小于该节点的值

也就是:[左子树的所有的节点] 该节点 [右子树的所有的节点]
是不是长的和快速排序有点像?

就是因为维持了这样的性质,达到了很好的效果:

  • 所有的数据是有序的
  • 查找数据的时间和二分查找几乎等同(平衡时相等)

为什么说平衡时相等?

考虑最极端的情况嘛。。就是一个链条,这样的时间复杂度就是O(N)了


如何达到自平衡的效果?

AVL树中引入了平衡因子的概念:

平衡因子:某个结点的左子树的高度减去右子树的高度得到的差值。

也就是说,一个平衡的树就是所有的节点的平衡因子的绝对值都是小于等于1的。

🌛 🌜:爱的魔力转圈圈~

旋转,通过旋转来达到自平衡。

问题来了,现在相信大家对于旋转是个什么鬼东西很疑惑

什么是旋转?

我们假设我们有一棵平衡的树,然后我们按照二叉搜索树的原则进行插入:我们大概率会破坏这棵树的平衡性。
最常见的一种情况便是:

1
2
3
4
5
   🤣                                    🤣
/ ---- > /
😜 😜
/
🙄

这简直太糟糕了,这样下去我们的可爱的BST就要变成链表了!
我们不能放纵我们的可爱的BST,不能让它随意生长,因此我们要:旋转。

在这里我们可以看出 🤣 > 😜 > 🙄,而这种情况下😜作为根节点(父节点)才是最优的选择。
因此我们就旋转一下:

1
2
3
4
5
      🤣                                    😜
/ ---- > / \
😜 🙄 🤣
/
🙄

这种情况称之为左左情况:在本来树向左倾斜的情况下又在左子树增加元素。进行右旋。
相同的:右右情况进行左旋。
左右情况:先左旋后右旋。
右左情况:先右旋后左旋。

如何实现?

节点 - Node

一个节点应该拥有哪些属性呢?

🤷‍♂️肯定要有左右子节点的指针吧!
🤷‍♂️要有平衡因子,嗯,这样才知道什么时候应该旋转。
🤷‍♂️为了方便计算平衡因子,应该缓存一下高度。

1
2
3
4
5
6
type Node struct {
Data int
Height int
Left *Node;
Right *Node;
}

递归计算节点的高度

1
2
3
4
5
6
func (root *Node)TreeHeight() int {
if root == nil {
return 0
}
return max(root.Left.TreeHeight(), root.Roght.TreeHeight()) + 1
}

计算平衡因子

1
2
3
4
5
6
func (root *Node)TreeGetBalanceFactor() int {
if root == nil {
return 0
}
return root.Left.Height - root.Right.Height
}

右旋

1
2
3
4
5
6
7
8
9
10
func (root *Node)TreeRotateRight() *Node {
left := root.Left

root.Left = Left.Right // 将将要被抛弃的节点连接为旋转后的 root 的左孩子
left.Right = root // 调换父子关系

left.Height = max(left.Left.TreeHeight(), left.Right.TreeHeight())+1
root.Right.Height = max(root.Right.Left.TreeHeight(), root.Right.Right.TreeHeight())+1
return left
}

左旋

1
2
3
4
5
6
7
8
9
10
func (root *Node)TreeRotateLeft() *Node {
right := root.Right

root.Right = right.Left
right.Left = root

root.Left.Height = max(root.Left.Left.TreeHeight(), root.Left.Right.TreeHeight())+1
right.Height = max(right.Left.TreeHeight(), right.Left.TreeHeight())+1
return right
}

搞懂了吗? 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️

Contents
  1. 1. 平衡还是不平衡,这是一个问题
  2. 2. 什么是二叉查找树?
    1. 2.1. 为什么说平衡时相等?
  3. 3. 如何达到自平衡的效果?
    1. 3.1. 什么是旋转?
  4. 4. 如何实现?
    1. 4.1. 节点 - Node
    2. 4.2. 递归计算节点的高度
    3. 4.3. 计算平衡因子
    4. 4.4. 右旋
    5. 4.5. 左旋
  5. 5. 搞懂了吗? 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️🤷🏻‍♀️ 🤷🏻‍♂️