您的位置:首页 > 理论基础 > 数据结构算法

非线性数据结构 之 AVL树(2)

2013-02-27 15:29 176 查看
这一节关注AVL树的删除操作,相对于插入和查找,AVL树的删除要复杂得多。

首先,如果不考虑“平衡性”,那么删除一个节点也不难,就是BST树的思路。

一开始,在树中查找这个节点,如果找不到,那就返回。如果找到了,那么再细分三种情况:

1. 这个待删除节点有左子树,那么我们要做的就是将其左子树中最大的节点替换该节点,然后再将左子树中的那个节点删除即可(递归算法):



2. 这个待删除节点没有左子树但有右子树,那么我们要做的就是将其右子树中最小的节点替换该节点,然后再将右子树中的那个节点删除即可(递归算法):



3, 要删除的节点既没有左子树也没有右子树,是一个叶子节点。这种情况最简单,直接删除即可。

上面三种情况,代码实现如下:

linktree AVL_remove(linktree root, tn_datatype data)
{
if(root == NULL)
return NULL;
// 以下是处理删除节点的代码
if(data < root->data)
root->lchild = AVL_remove(root->lchild, data); // 如果待删除节点比当前节点小,那么去左子树中删除
else if(data > root->data)
root->rchild = AVL_remove(root->rchild, data); // 如果待删除节点比当前节点大,那么去右子树中删除
else
{
linktree p;

if(root->lchild != NULL) // 1. 待删除的节点(红色)有左子树
{
for(p=root->lchild; p->rchild!=NULL; p=p->rchild){;} // 找到左子树中最大的节点(蓝色)
root->data = p->data; // 替换待删除节点
root->lchild = AVL_remove(root->lchild, p->data); // 在左子树中将该节点(蓝色)删除
}
else if(root->rchild != NULL) // 2. 待删除的节点没有左子树但是有右子树,以下步骤跟上面的是对称的,不敖述
{
for(p=root->rchild; p->lchild!=NULL; p=p->lchild){;}
root->data = p->data;
root->rchild = AVL_remove(root->rchild, p->data);
}
else // 3. 待删除的节点是叶子,直接删除。
{
free(root);
return NULL;
}
}

// 以下是处理平衡性的代码
// ...
// ...
}


节点删除了,下面要考虑平衡性的问题。情况有以下几种:

第一,删除节点之后,其左子树太高,导致树不平衡:(黑色的节点表示刚刚删除了某个节点的子树的根)



如图所示,在这种情况下,还要细分两种情况,情形一是黑色节点的左子树有左子树,这种情况下只需要将黑色节点右旋转即可恢复平衡。情形二是黑色节点的左子树没有左子树但是有右子树,这种情况下要先将其左子树左旋转,再对其进行右旋转。待会儿细述。

第二,完全对称地,删除节点后,其右子树太高,导致树不平衡:



同样,如图所示,当在子树中删除某个节点之后,可能会导致如图所示的不平衡的情况,这时其右子树太高。而两种情形的处理几乎跟上面是一样的,即情形一只需要对黑色的根节点进行一次左旋转即可,而情形二则需要先对其右孩子进行右旋转,再对其进行左旋转就可恢复平衡。

有人可能会疑问,为什么删除之后不平衡只会是这两种情况? 难道不会有其他的情况了吗? 是的没有了,上面的两种情况四种情形已经包括了所有的可能性。如果你没想明白为什么会那么简单,那是因为你没考虑这是一个递归的算法,以上情形都是发生在最后的递归的分支上。然后顺藤摸瓜层层递归,从叶子开始往上到根节点不断进行重新平衡。

以下是重新平衡的代码:

linktree AVL_remove(linktree root, tn_datatype data)
{
if(root == NULL)
return NULL;

// 以下是处理删除节点的代码
// ...
// ...

// 以下是处理平衡性的代码
if(height(root->lchild) - height(root->rchild) == 2) // 左子树太高导致不平衡
{
if(height(root->lchild->rchild)-height(root->lchild->rchild) == 1) // 情形二
root = LR(root);
else // 情形一
root = LL(root);
}
else if(height(root->rchild) - height(root->lchild) == 2) // 右子树太高导致不平衡
{
if(height(root->rchild->lchild)-height(root->rchild->rchild) == 1) // 情形二
root = RL(root);
else // 情形一
root = RR(root);
}

root->bf = MAX(height(root->lchild), height(root->rchild)) + 1;
return root;
}


将上述两部分代码合并起来,就是AVL完整的删除算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息