一个二叉树的层数越少,那么他查询的速度就会更快。所以我们要尽可能的去减少二叉树层数,同时还要保证所有的元素都在这棵树上。所以我们才需要去做二叉搜索树的平衡。我们的目的也正是减少二叉树的层数。
完全二叉树,满二叉树这些都是AVL树。他们满足AVL树的所有条件
对于AVL树不是很了解的可以先看下我二叉平衡树文章
左旋
例1:
2
\
4
/ \
3 5
根节点为2
,左深度为0
,右深度为2
。左右深度相差大于1
。要向左平衡,即左旋。
右旋
例2:
2
/
4
/ \
3 5
根节点为2
,左深度为2
,右深度为0
。左右深度相差大于1
。要向右平衡,即右旋。
通过上面的两种旋转,并不能100%的构建出AVL树。因为还有其他情况。比如:
例3:
2
/ \
1 5
/ \
4 6
/
3
首先对于节点2
不平衡,需要向左旋转。左旋之后:
例4:
5
/ \
2 6
/ \
1 4
/
3
仍然不平衡,这种特殊的情况,还需做以下的操作。
左右双旋
在例3中,有以下几个特点:
- 二叉树左右深度相差超过2
- 节点
4
是原来5
唯一最深的子节点。左旋之后需要更改节点4
。由节点5
的left改为节点2
的right。
当满足以上条件时需要先对节点5
进行一次右旋
例5:
2
/ \
1 4
/ \
3 5
\
6
然后再进行左旋
例6:
4
/ \
2 5
/ \ \
1 3 6
这时才是一棵平衡树
右左双旋
例7:
9
/ \
5 10
/ \
4 6
\
7
在例7中,有以下几个特点:
- 二叉树左右深度相差超过2
- 节点
6
是原来5
唯一最深的子节点。左旋之后需要更改节点6
。由节点5
的right9
的left。
当满足以上条件时需要先对节点5
进行一次左旋
9
/ \
6 10
/ \
5 7
/
4
然后再进行右旋
6
/ \
5 9
/ / \
4 7 10
这时才是一棵平衡树
通过上面的左旋
,左右旋
,右旋
,右左旋
之后树的结构发生了变化,很可能导致旋转后仍然不平衡。所以需要再进行一次同向旋转。
右右双旋
例8:
5
/ \
2 6
/ \
1 3
/ \
0 4
右旋后
2
/ \
1 5
/ \
3 6
/ \
0 4
此时仍然不满足AVL树。需要再对根节点2
的right
进行一次旋转(不一定是左旋或者右旋,根据左右的子节点的深度进行判断)
左左双选
与右右双旋
同理,在左旋后需要对左子树再进行一次旋转(不一定是左旋或者右旋,根据左右的子节点的深度进行判断)
我们最好再进行一次平衡判断来确保万无一失
代码实现
/**
* 构造节点
*/
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
/**
* 获取节点最大深度
*/
function getDeep(root) {
if (root === null) {
return 0;
}
const left = getDeep(root.left);
const right = getDeep(root.right);
return Math.max(left, right) + 1;
}
/**
* 判断是节点否为平衡二叉树
*/
function isBalance(root) {
if(root === null) {
return true;
}
const leftDeep = getDeep(root.left);
const rightDeep = getDeep(root.right);
if(Math.abs(leftDeep - rightDeep) > 1) {
return false;
}
return isBalance(root.left) && isBalance(root.right);
}
/**
* 左旋
*/
function rotateLeft(root) {
const temp = root.right;
temp.left = root;
root.right = temp.left;
return temp;
}
/**
* 右旋
*/
function rotateRight(root) {
const temp = root.left;
temp.right = root;
root.left = temp.right;
return temp;
}
/**
* 调整二叉树为AVL
*/
function change(root){
if(isBalance(root)) {
return root;
}
if(root.right !== null) {
// 对右子树进行递归调整
root.right = change(root.right)
}
if(root.left !== null) {
// 对左子树进行递归调整
root.left = change(root.left)
}
const leftDeep = getDeep(root.left)
const rightDeep = getDeep(root.right)
if(leftDeep > rightDeep) {
// 右旋时需要被改变的节点(root.left.right)
const changeTreeDeep = getDeep(root.left.right)
// 右旋时不需要被改变的节点(root.left.left)
const unChangeTreeDeep = getDeep(root.left.left)
if(changeTreeDeep > unChangeTreeDeep) {
// 右左旋转
root.left = rotateLeft(root.left);
}
// 右旋
const newRoot = rotateRight(root);
// 右右旋转
newRoot.right = change(newRoot.right);
// 再次平衡确认
return change(newRoot)
} else {
// 左旋时需要被改变的节点(root.right.left)
const changeTreeDeep = getDeep(root.right.left)
// 右旋时不需要被改变的节点(root.right.right)
const unChangeTreeDeep = getDeep(root.left.left)
if(changeTreeDeep > unChangeTreeDeep) {
// 左右旋转
root.right = rotateLeft(root.right);
}
// 左旋
const newRoot = rotateLeft(root);
// 左左旋转
newRoot.left = change(newRoot.left);
// 再次平衡确认
return change(newRoot)
}
}
/**
* 2
* / \
* 1 4
* / \
* 3 5
*/
const a1 = new Node(2)
const b1 = new Node(3)
const c1 = new Node(4)
const d1 = new Node(5)
const e1 = new Node(1)
a1.left = e1;
a1.right = c1;
c1.left = b1;
c1.right = d1;
console.log(isBalance(change(a1)));
// true