版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xsp_happyboy/article/details/82113818
1 AVL树
在二叉查找树中,为了防止某节点处出现左、右儿子树深度不平衡的情况,从而引出带有平衡条件的二叉查找树(AVL树)。
回顾树结构基本知识:
对任意的节点n,n的深度为:从根到节点n的唯一路径的长。n的高度为:从n到一片树叶的最长路径的长。
AVL树的平衡条件:每个节点的左子树和右子树的高度最多差1。
2 单旋、双旋
2.1 单旋
在插入一个节点后,只有那些从插入点到根节点路径上的节点的平衡性可能会改变。从插入点开始,沿着这条路径往上寻找到第一个不平衡的点(即最深的不平衡节点),接下来,我们将在该节点处平衡这棵树。
需进行单旋调整,情形一:
情形二:
特点:从不平衡点到插入点的走向一致
平衡操作:分析从不平衡点到插入点的这颗子树,根节点和树叶都是极值点,因此取中间节点(图中的节点7)为新的平衡点进行调整。
平衡之后的树:
2.2 双旋
需要进行双旋调整,情形一:
情形二:
特点:从不平衡点到插入点的走向不一致
平衡操作:分析从不平衡点到插入点的这颗子树,根节点和中间节点都是极值点,因此取插入点(图中的节点7)为新的平衡点(新的根)进行调整。
2.3 单旋、双旋规律
插入节点(记做i节点)后,找到第一个不平衡的节点(记做a节点),从a节点到i 节点的路径上,从a节点开始往下数,把包括a节点在内的连续三个节点,作为参与旋转的节点,并依次称为a、b、c节点。
对于单旋情形:由于a、c都是极值点,故取b作为新的子树的根节点。
对于双旋情形:由于a、b都是极值点,故取c作为新的子树的根节点。
3 C语言实现
3.1 Insert例程
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
struct AvlNode{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;//节点高度
}
/*
* 获取节点高度
*/
static int
Height(Position P){
if(P == NULL){
return -1;
}
return P -> Height;
}
/**
* 插入例程
* 输入参数:插入元素,树根节点
* 返回参数:树根节点
*/
AvlTree
Insert(ElementType X,AvlTree T){
if(T == NULL){
/*
* 一般情况为递归调用的最后一层,执行插入操作
*/
T = malloc(sizeof(struct AvlNode));
if(T == NULL){
//内存不足异常
}else{
T -> Element = X;
T -> Left = T -> Right = NULL;
T -> Height = 0;
}
}else if(X < T -> Element){
/*
* 往左边递归插入
*/
T -> Left = Insert(X,T -> Left);
//插入完成判断当前节点下的左子树与右子树的高度差,确定不平衡点
if(Height(T -> Left) - Height(T -> Right) == 2){
//中间节点(T的左边节点)大于插入点元素,单旋
if(X < T -> Left -> Element){
T = SingleRotateWithLeft(T);
}else{
T = dubbleRotateWithLeft(T);
}
}
}else if(X > T -> Element){
/*
* 往右边递归插入
*/
T -> Right = Insert(X,T -> Right);
if(Height(T -> Right) - Height(T -> Left) == 2){
if(X > T -> Right -> Element){
T = SingleRotateWithRight(T);
}else{
T = dubbleRotateWithRight(T);
}
}
}
//返回T的高度,T的两儿子树中最大高度 + 1
T -> Height = Max(Height(T -> Left),Height(T -> Right)) + 1;
return T;
}
/*
* 单旋
*/
static Position
SingleRotateWithLeft(Postion k1){
Position k2 = k1 -> Left;
k1 -> Left = k2 -> Right;
k2 -> Right = k1;
k1 -> Height = Max(Height(k1 -> Left),Height(k1 -> Right)) + 1;
k2 -> Height = Max(Height(k2 -> Left),Height(k1)) + 1;
return k2;
}
/*
* 双旋分解成两次单旋
*/
static Position
DoubleRotateWithLeft(Postion k3){
//对k3的左子树进行单旋操作
k3 -> Left = SingleRotateWithRight(k3 -> Left);
//对k3进行单旋
return SingleRotateWithLeft(k3);
}
3.2 Delete例程
AvlTree
Delete(ElementType X,AvlTree T){
Position temp;
if(T == NULL){
// 元素未找到异常
}else if(X < T -> Element){
T -> Left = Delete(X,T -> Left);
// 判断目标节点是否不平衡
if(Height(T -> Right) - Height(T -> Left) == 2){
/* 若不平衡,则比较目标节点(T节点)的右节点的,左、右儿子树的高度 */
/* 若右儿子树高度大于左儿子树高度,单旋操作 */
/* 若左儿子树高度大于右儿子树高度,双旋操作 */
/* 若左、右儿子树高度相等,单旋、双旋都可(默认单旋)*/
if(Height(T -> Right -> Right) < Height(T -> Right -> Left) ){
T = DoubleRotateWithRight(T);
}else{
T = SingleRotateWithRight(T);
}
}
}else if(X > T -> Element){
T -> Right = Delete(X,T -> Right);
// 判断目标节点是否不平衡
if(Height(T -> Left) - Height(T -> Right) == 2){
if(Height(T -> Left -> Right) > Height(T -> Left -> Left) ){
T = DoubleRotateWithLeft(T);
}else{
T = SingleRotateWithLeft(T);
}
}
}else if((T -> Left != NULL) && (T -> Right != NULL)){
/* 目标节点匹配到元素X,且有两个儿子树 */
//找到目标节点右子树的最小节点
temp = FindMin(T -> Right);
//最小节点的元素覆盖目标节点元素
T -> ELement = temp -> Element;
//递归调用,删除右子树中的最小节点
T -> Right = Delete(T -> Element,T -> Right);
// 判断目标节点是否不平衡
if(Height(T -> Left) - Height(T -> Right) == 2){
if(Height(T -> Left -> Right) > Height(T -> Left -> Left) ){
T = DoubleRotateWithLeft(T);
}else{
T = SingleRotateWithLeft(T);
}
}
}else{
/* 目标节点匹配到元素X,且有一个儿子树,或没有儿子树 */
/* 目标节点最多只有一个儿子树,因此左子树高度为-1,右子树高度必为0 */
/* 所以该目标节点处,不会发生不平衡情况 */
temp = T;
if(T -> Left == NULL){
T = T -> Right;
}else if(T -> Right == NULL){
T = T -> Left;
}
free(temp);
}
// 计算高度
T -> Height = Max(Height(T -> Left),Height(T -> Right)) + 1;
return T;
}