AVL树C语言实现

AVL树介绍

AVL树是带有平衡条件的二叉查找树。这个平衡条件必须容易保持,而且它必须保证树的高度是O(logN)。AVL树其每个节点的左子树和右子树高度最多差1(空树的高度为-1),每个节点保留其高度信息。下图中,左边的图是AVL树,而右边的不是。

《AVL树C语言实现》

节点插入

当对一个AVL树插入节点后,只有那些从插入节点到根节点的路径上的节点的平衡可能被改变,因为这些节点的子树可能发生变化。当我们沿着这条路径上行到根并更新平衡信息时,我们可以找到第一个节点,它的新平衡破坏了AVL条件。下面指出如何调整这个节点。

假设必须重新平衡的节点叫做A。由于任意节点最多有两个孩子,因此高度不平衡时,A点的两个子树的高度差2.容易看出,这种不平衡可能出现在下面四种情况中:

1):对A的左儿子的左子树进行了一次插入,可通过一次单旋调整;

2):对A的左儿子的右子树进行了一次插入,可通过一次双旋调整

3):对A的右儿子的左子树进行了一次插入,可通过一次双旋调整

4):对A的右儿子的右子树进行了一次插入,可通过一次单旋调整;

其中,情形1和4是关于A点的镜像对称,情形2和3也是关于A点的镜像对称。单旋较简单,如示例图1所示:首先找到不满足平衡条件的节点(节点3),然后在该节点与该节点的儿子之间(节点4)进行一次单选操作即可。

《AVL树C语言实现》

图1:情形4所进行的单旋转操作

对于双旋,其实用一句话就可以简单概括双旋:假如A点是破坏平衡条件的第一个需要调整的节点,那么双旋发生的是:首先在A的儿子和孙子之间做一次单旋,然后在A和它的新儿子之间再做一次单旋。如示例图2所示,插入节点14之后,节点6不在满足平衡条件。因为是在6的右儿子的左子树进行插入,所以需要进行一次右左双旋。首先在节点6的儿子(节点15)和孙子(节点7)之间进行一次右旋,得到中间结果,然后将节点6和其新儿子(节点7)之间进行左旋,得到最终结果。右左双旋也是同样的道理。

《AVL树C语言实现》《AVL树C语言实现》

图2 双旋操作

节点删除

回顾一下对于一般的二叉查找树是怎样进行remove操作的。先根据二叉树的性质找到应该删除的结点t(如果有的话):如果t是个叶子结点,直接delete就好;如果它只有一颗非空的子树,那么就让这颗子树继承被删除结点的位子;而如果它有两颗非空的子树,就在右子树X中找到值最小的结点s,将s的data(值)写到t中,再在X中删除s(因为s的左子树一定为空,否则它就不是X中最小的了。另外,当然也可以在左子树中找值最大的结点)

《AVL树C语言实现》

对于AVL树的删除操作,可概括为如下几种情况:

首先在树中搜寻是否有节点的元素值等于需要删除的元素。如未搜索到,直接返回。否则执行以下操作。

(1)  要删除的节点是当前根节点T。

            如果左右子树都非空。在高度较大的子树中实施删除操作。分以下两种情况:

                    A、左子树高度大于右子树高度,将左子树中最大的那个元素赋给当前根节点,然后删除左子树中元素值最大的那个节点。

                    B、左子树高度小于右子树高度,将右子树中最小的那个元素赋给当前根节点,然后删除右子树中元素值最小的那个节点。

          如果左右子树中有一个为空,那么直接用那个非空子树或者是NULL替换当前根节点即可。

(2)  要删除的节点元素值小于当前根节点T值,在左子树中进行删除。

            递归调用,在左子树中实施删除。

            这个是需要判断当前根节点是否仍然满足平衡条件,如果满足平衡条件,只需要更新当前根节点T的高度信息。 否则,需要进行旋转调整:

            如果T的右子节点的左子树的高度大于 T的右子节点的右子树的高度,进行相应的双旋转。否则进行单旋转。

(3)  要删除的节点元素值大于当前根节点T值,在右子树中进行删除。如果T的左子节点的右子树高度大于左子节点的左子树高度,进行双旋,否则进行单旋。过程和(2)类似

(4)  回朔被删除节点到根的路径上的其他节点,检查其平衡因子是否满足要求。

下面通过例子简要说明:

例1:如上图,若删除节点8,则其根节点9仍满足高度平衡,因此不需要旋转,简单调整元素9的平衡因子。然后再删除12,此时符合情况(3),11元素为当前的根T,令它的左子树为S,检查S的左子节点的高度HL与右子节点的高度HR,上图中可以看出HL = -1,HR = 0,所以符合双旋条件(HL < HR)。

例2:如果直接在原图上删除12,那么当前的根仍是11,但此时,11的平衡因子被打破,仍然符合情况(3),此时T的左子树为S,S的左子树高度HL = 0,S的 右子树高度为0,此时不满足双旋条件,故在根12处单旋。

代码实现

头文件:AvlTree.h

typedef int ElementType;

#ifndef _AVLTREE_
#define _AVLTREE_

struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;

AvlTree MakeEmpty( AvlTree T );
Position Find( ElementType X, AvlTree T );
Position FindMin( AvlTree T );
Position FindMax( AvlTree T );
AvlTree Insert( ElementType X, AvlTree T );
AvlTree Remove( ElementType X, AvlTree T );
ElementType Retrieve( Position P );
void PrintTree(AvlTree T);
AvlTree Remove(const ElementType X, AvlTree T);

#endif

实现文件:AvlTree.c

#include "AvlTree.h"
#include <stdio.h>
#include <stdlib.h>

struct AvlNode
{
    ElementType Element;
    AvlTree Left;
    AvlTree Right;
    int Height;
};

AvlTree MakeEmpty( AvlTree T )
{
    if (T != NULL)
    {
        MakeEmpty(T->Left);
        MakeEmpty(T->Right);
        free(T);
    }
    return NULL;
}

Position Find( ElementType X, AvlTree T )
{
    if (T == NULL)
        return NULL;
    if (X < T->Element)
        return Find(X, T->Left);
    else if (X > T->Element)
        return Find(X, T->Right);
    else
        return T;
}

Position FindMin( AvlTree T )
{
    if (T == NULL)
        return NULL;
    /*if (T->Left == NULL)
        return T;
    else
        return FindMin(T->Left);*/
    while (T->Left != NULL)
        T = T->Left;
    return T;
}

Position FindMax( AvlTree T )
{
    if (T == NULL)
        return NULL;
    /*if (T->Right == NULL)
        return T;
    else
        return FindMax(T->Right);*/
    while (T->Right)
        T = T->Right;
    return T;
}

static int Height( Position P )
{
    if (P == NULL)
        return -1;
    else
        return P->Height;
}

static int Max( int Lhs, int Rhs )
{
    return Lhs > Rhs ? Lhs : Rhs;
}

static Position SingleRotateWithLeft( Position K2)
{
    Position K1;
    K1 = K2->Left;
    K2->Left = K1->Right;
    K1->Right = K2;
    K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
    K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
    return K1;
}

static Position SingleRotateWithRight( Position K1)
{
    Position K2 = K1->Right;
    K1->Right = K2->Left;
    K2->Left = K1;
    K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
    K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
    return K2;
}

/* Do the left-right double rotation */
static Position DoubleRotateWithLeft( Position K3 )
{
    K3->Left = SingleRotateWithRight(K3->Left);
    return SingleRotateWithLeft(K3);
}

/* Do the right-left double rotation */
static Position DoubleRotateWithRight( Position K1 )
{
    K1->Right = SingleRotateWithLeft(K1->Right);
    return SingleRotateWithRight(K1);
}

AvlTree Insert( ElementType X, AvlTree T )
{
    if (T == NULL)
    {
        T = (AvlTree)malloc(sizeof(struct AvlNode));
        if (T == NULL)
        {
            printf("Out of space!!\n");
            return T;
        }
        else
        {
            T->Left = T->Right = NULL;
            T->Element = X;
        }
    }
    else if (X < T->Element)
    {
        T->Left = Insert(X, T->Left);
        if (Height(T->Left) - Height(T->Right) == 2)
        {    
            //插入之后,引起不平衡,若是在左儿子的左子树中插入,即左-左,只需左单旋即可;
            if (X < T->Left->Element)
                T = SingleRotateWithLeft(T);
            else
            //若是在左儿子的右子树中插入,即左-右,需要左双旋
                T = DoubleRotateWithLeft(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 = DoubleRotateWithRight(T);
        }
    }
    T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
    return T;
}

ElementType Retrieve( Position P )
{
    return P->Element;
}

void PrintTree(AvlTree T)
{
    if (T != NULL)
    {
        PrintTree(T->Left);
        printf("%3d", Retrieve(T));
        PrintTree(T->Right);
    }
}

AvlTree Remove(ElementType X, AvlTree T)
{
      if(T == NULL)
          return NULL;//没有找到要删除的值,do nothing
      if(X < T->Element)
      {
          T->Left = Remove(X, T->Left);
          if(Height(T->Right)-Height(T->Left)==2)
          {
              //右子树比左子树高2,那么在删除操作之前右子树比左子树高1,
             //也就是说T的右子树必然不为空,甚至必然有非空子树(高度至少为1).
             AvlTree s = T->Right;
             if(Height(s->Left)>Height(s->Right))
                 T = DoubleRotateWithRight(T);//右双旋转
             else
                 T = SingleRotateWithRight(T);//右单旋转
         }
         else
             //不需要调整就满足平衡条件的话,只需要重新计算其高度就好
             T->Height=Max(Height(T->Left),Height(T->Right))+1;
     }
     else if(X>T->Element)
     {

         T->Right = Remove(X,T->Right);
         if(Height(T->Left)-Height(T->Right)==2)
         {
             AvlTree s=T->Left;
             if(Height(s->Right)>Height(s->Left))
                 T = DoubleRotateWithLeft(T);//左双旋转
             else
                 T = SingleRotateWithLeft(T);//左单旋转
         }
         else
             T->Height=Max(Height(T->Left),Height(T->Right))+1;
     }
     else
     {
         if(T->Left&&T->Right)         //T的左右子树都非空,把Remove操作转移到只有一个非空子树的结点或者叶子结点上去
         {
             if( Height(T->Left) > Height(T->Right) )
             //把Remove操作往更高的那颗子树上转移
             {
                 //左子树中的最大值
                 T->Element=FindMax(T->Left)->Element;
                 T->Left = Remove(T->Element,T->Left);
             }
             else
             {
                 //右子树中的最小值
                 T->Element=FindMin(T->Right)->Element;
                 T->Right = Remove(T->Element,T->Right);
             }
         }
         else
         {
             AvlTree oldnode = T;
             T=T->Left?T->Left:T->Right;
             free(oldnode);
         }
     }
    return T;
}

    原文作者:AVL树
    原文地址: https://blog.csdn.net/universitycd/article/details/8869842
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞