为什么需要平衡二叉树?
首先我们思考一个问题,为什么要学习平衡二叉树(AVL),是二叉排序树(BST)不香嘛?
哎,确实,确实不香。
二叉排序树存在一个致命缺点,如下图所示这种情况:
emmmm,这确实也是一个二叉排序树,但是乍一看,害,这不整一条链表过来装树嘛!
在这种情况下,二叉排序树退化成一条链表
链表的特点是什么?(给你三秒种反应)
3
2
1
对了!增删快、查找慢,这与我们建立二叉排序树的初衷相悖
为此,我们引入了今天的主角,平衡二叉树(Self-balancing binary search tree)!
基本介绍
- 平衡二叉树也叫平衡二叉搜索树,又被称为AVL树,可以保证查询效率较高
- 具有以下特点:
- 是一颗空树或左右两个子树的高度差的绝对值不超过1
- 左右两个子树都是一颗平衡二叉树
平衡二叉树的实现方法:红黑树(HashMap、TreeMap底层都有用到)、AVL、替罪羊树、Treap、伸展树
应用实例
每当 平衡二叉树满足:
$$
|rightHeight() - leftHeight()| > 1
$$
时,不再是一颗平衡二叉树,需要进行旋转操作使其恢复平衡。
左旋(降低右子树的高度):
给定数列 {4,3,6,5,7,8},创建出对应的二叉平衡树
注意到当插入 8 时,不满足平衡二叉树的条件,此时需要对二叉树进行左旋,才能使其恢复平衡
具体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
Node newNode = new Node(4);
newNode.left = this.left;
newNode.right = this.right.left;
this.value = right.value;
this.right = this.right.right;
this.left = newNode;
|
右旋(降低左子树的高度):
给定数列 {10,12,8,9,7,6} 创建对应平衡二叉树
当插入节点 6 时,不满足平衡二叉树的条件,此时需要对二叉树进行右旋,才能使其恢复平衡
具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
Node newNode = new Node(10);
newNode.right = this.right;
newNode.left = this.left.right;
this.value = right.value;
this.left = this.left.left;
this.right = newNode;
|
总结一下左旋和右旋的实现:
- 创建新节点
- 设置新节点的左右子树
- 设置当前节点的左右子树
双旋转
前面的两个实例都可以通过一次旋转将非平衡二叉树转化为平衡二叉树,但是在一些情况下,单旋转不能完成平衡二叉树的转换,比如数列 {10,11,7,6,8,9}、{2,1,6,5,7,3}
问题分析:
- 当符合右旋条件时
- 如果当前节点的左子树的右子树高于当前节点的右子树
- 应当先对当前节点的左子树进行左旋
- 再对当前节点进行右旋
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
public int leftHeight() { if (this.left == null) { return 0; } return this.left.height(); }
public int rightHeight() { if (this.right == null) { return 0; } return this.right.height(); }
public int height() { return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1; }
public void leftRotate() { Node newNode = new Node(this.value); newNode.left = this.left; newNode.right = this.right.left; this.value = this.right.value; this.right = this.right.right; this.left = newNode; }
public void rightRotate() { Node newNode = new Node(this.value); newNode.right = this.right; newNode.left = this.left.right; this.value = this.right.value; this.left = this.left.left; this.right = newNode; }
public void add(Node node) { if (node == null) { return; } if (node.value < this.value) { if (this.left == null) { this.left = node; } else { this.left.add(node); } } else { if (this.right == null) { this.right = node; } else { this.right.add(node); } }
if (rightHeight() - leftHeight() > 1) { if (right != null && right.leftHeight() - right.rightHeight() > 1) { right.rightRotate(); leftRotate(); } else { leftRotate(); } return; }
if (leftHeight() - rightHeight() > 1) { if (left != null && left.rightHeight() - left.leftHeight() > 1) { left.leftRotate(); rightRotate(); } else { rightRotate(); } } }
|
参考
尚硅谷:https://www.bilibili.com/video/BV1E4411H73v