AvlTree

定义

AVL 树是一种平衡二叉树,得名于其发明者的名字( Adelson-Velskii 以及 Landis)。(可见名字长的好处,命名都能多占一个字母出来)。平衡二叉树递归定义如下:

  1. 左右子树的高度差小于等于 1。
  2. 其每一个子树均为平衡二叉树。

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

AVL 树: 所有结点的平衡因子的绝对值都不超过 1 的二叉树。

为了计算平衡因子,我们自然需要在节点中引入高度这一属性。在这里,我们把节点的高度定义为其左右子树的高度的最大值。因此,引入了高度属性的 AVL 树的节点定义如下:

1
2
3
4
5
6
typedef struct node {
int data;
int height;
struct node *left;
struct node *right;
}node_t, * nodeptr_t;

计算某一个节点的高度

1
2
3
4
5
6
7
int treeHeight(nodeptr_t root) {
if(root == NULL) {
return 0;
} else {
return max(treeHeight(root->left),treeHeight(root->right)) + 1;
}
}

我们在进行如下操作时需要更新受影响的所有节点的高度:

  1. 在插入结点时, 沿插入的路径更新结点的高度值
  2. 在删除结点时(delete),沿删除的路径更新结点的高度值

有了高度,计算平衡因子的操作就得以很简单的实现:

1
2
3
4
5
6
int treeGetBalanceFactor(nodeptr_t root) {
if(root == NULL)
return 0;
else
return x->left->height - x->right->height;
}

当平衡因子的绝对值大于 1 时,就会触发树的修正,或者说是再平衡操作。

树的平衡化操作

二叉树的平衡化有两大基础操作: 左旋和右旋。左旋,即是逆时针旋转;右旋,即是顺时针旋转。这种旋转在整个平衡化过程中可能进行一次或多次,这两种操作都是从失去平衡的最小子树根结点开始的(即离插入结点最近且平衡因子超过1的祖结点)。

右旋

img

所谓右旋操作,就是把上图中的 B 节点和 C 节点进行所谓“父子交换”。在仅有这三个节点时候,是十分简单的。但是当 B 节点处存在右孩子时,事情就变得有点复杂了。我们通常的操作是:抛弃右孩子,将之和旋转后的节点 C 相连,成为节点 C 的左孩子。这样,我们就能写出对应的代码。

1
2
3
4
5
6
7
8
9
10
11
nodeptr_t treeRotateRight(nodeptr_t root) {
nodeptr_t left = root->left;

root->left = left->right; // 将将要被抛弃的节点连接为旋转后的 root 的左孩子
left->right = root; // 调换父子关系

left->height = max(treeHeight(left->left), treeHeight(left->right))+1;
right->height = max(treeHeight(right->left), treeHeight(right->right))+1;

return left;
}

左旋

img

左旋操作和右旋操作十分类似

1
2
3
4
5
6
7
8
9
10
11
nodeptr_t treeRotateLeft(nodeptr_t root) {
nodeptr_t right = root->right;

root->right = right->left;
right->left = root;

left->height = max(treeHeight(left->left), treeHeight(left->right))+1;
right->height = max(treeHeight(right->left), treeHeight(right->right))+1;

return right;
}

需要平衡的四种情况

LL

img

RR

img

LR

img

RL

实现

平衡化操作的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nodeptr_t treeRebalance(nodeptr_t root) {
int factor = treeGetBalanceFactor(root);
if(factor > 1 && treeGetBalanceFactor(root->left) > 0) // LL
return treeRotateRight(root);
else if(factor > 1 && treeGetBalanceFactor(root->left) <= 0) { //LR
root->left = treeRotateLeft(root->left);
return treeRotateRight(temp);
} else if(factor < -1 && treeGetBalanceFactor(root->right) <= 0) // RR
return treeRotateLeft(root);
else if((factor < -1 && treeGetBalanceFactor(root->right) > 0) { // RL
root->right = treeRotateRight(root->right);
return treeRotateLeft(root);
} else { // Nothing happened.
return root;
}
}

插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void treeInsert(nodeptr_t *rootptr, int value)
{
nodeptr_t newNode;
nodeptr_t root = *rootptr;

if(root == NULL) {
newNode = malloc(sizeof(node_t));
assert(newNode);

newNode->data = value;
newNode->left = newNode->right = NULL;

*rootptr = newNode;
} else if(root->data == value) {
return;
} else {
if(root->data < value)
treeInsert(&root->right,value);
else
treeInsert(&root->left,value)
}

treeRebalance(root);
}

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void treeDelete(nodeptr_t *rootptr, int data)
{
nodeptr_t *toFree; // 拜拜了您呐
nodeptr_t root = *rootptr;

if(root) {
if(root->data == value) {
if(root->right) {
root->data = treeDeleteMin(&(root->right));
} else {
toFree = root;
*rootptr = toFree->left;
free(toFree);
}
} else {
if(root->data < value)
treeDelete(&root->right,value);
else
treeDelete(&root->left,value)
}

treeRebalance(root);
}
}