至于什么是AVL树和AVL树的一些概念问题在这里就不多说了,下面是我写的代码,里面的注释非常详细地说明了实现的思想和方法。

因为在操作时真正需要的是子树高度的差,所以这里采用-1,0,1来表示左子树和右子树的高度差,而没有使用记录树的高度的方法。

代码如下:

typedef struct AVLNode
{
    DataType cData;
    int nBf;        //结点的平衡因子,-1表示右子树的深度比左子树高1
                    //0表示左子树与右子树深度相等
                    //1表示左子树的深度比右子树高1
    struct AVLNode *LChild;
    struct AVLNode *RChild;
}AVLNode,*AVLTree;

typedef int BOOL;

enum enumHight{LH = 1, EH = 0, RH = -1} eHight;
enum enumBool{FALSE = 0, TRUE = 1} eBool;

void R_Rotate(AVLTree *pAT)
{
    //对以*pAT为根的二叉树作右旋转处理,处理之后pAT指向新的树根结点
    //即旋转处理之前的左子树的根结点
    AVLTree lc = (*pAT)->LChild;
    (*pAT)->LChild = lc->RChild;
    lc->RChild = *pAT;
    *pAT = lc;
}
void L_Rotate(AVLTree *pAT)
{
    //对以*pAT为根的二叉树作左旋转处理,处理之后pAT指向新的树根结点
    //即旋转处理之前的右子树的根结点
    AVLTree rc = (*pAT)->RChild;
    (*pAT)->RChild = rc->LChild;
    rc->LChild = *pAT;
    *pAT = rc;
}
void LeftBalance(AVLTree *pAT)
{
    //对以指针pAT所指结点为根的二叉树作左平衡旋转处理,
    //本算法结束时指针pAT指向新的结点
    AVLTree lc = (*pAT)->LChild;    //lc指向*pAT的左子树根结点
    AVLTree rd = NULL;
    if(lc)
    switch(lc->nBf)     //检查*pAT的左子树的平衡度,并作相应平衡处理
    {
        case LH:    //新结点插入在*pAT的左孩子的左子树上,要作单右旋转处理
            (*pAT)->nBf = lc->nBf = EH;
            R_Rotate(pAT);
            break;
        case RH:    //新结点插入在*pAT的左孩子的右子树上,要作双旋转处理
            rd = lc->RChild;
            switch(rd->nBf) //修改*pAT及其左孩子的平衡因子
            {
                case LH:
                    (*pAT)->nBf = RH;
                    lc->nBf = EH;
                    break;
                case EH:
                    (*pAT)->nBf = lc->nBf = EH;
                    break;
                case RH:
                    (*pAT)->nBf = EH;
                    lc->nBf = LH;
                    break;
                default:;
            }
            rd->nBf = EH;
            L_Rotate(&(*pAT)->LChild);  //对*pAT的左子树作左平衡处理
            R_Rotate(pAT);      //对*pAT作右平衡处理
            break;
        default:;
    }
}
void RightBalance(AVLTree *pAT)
{
    //对以指针pAT所指结点为根的二叉树作右平衡旋转处理,
    //本算法结束时指针pAT指向新的结点
    AVLTree rc = (*pAT)->RChild;
    AVLTree rd = NULL;
    if(rc)
    switch(rc->nBf)
    {
        case RH:
            (*pAT)->nBf = rc->nBf = EH;
            L_Rotate(pAT);
            break;
        case LH:
            rd = rc->LChild;
            switch(rd->nBf)
            {
                case LH:
                    (*pAT)->nBf = EH;
                    rc->nBf = RH;
                    break;
                case EH:
                    (*pAT)->nBf = rc->nBf = EH;
                    break;
                case RH:
                    (*pAT)->nBf = LH;
                    rc->nBf = EH;
                    break;
            }
            rd->nBf = EH;
            R_Rotate(&(*pAT)->RChild);
            L_Rotate(pAT);
        default:;
    }
}
BOOL InsertATNode(AVLTree *pAT, DataType c)
{
    //若在平衡的二叉树pAT中不存在和c相同的关键字结点,
    //则插入一个以c为数据元素的新结点,并返回1,否则返回0
    //若因插入而使二叉排序树失去平衡,则作平衡旋转处理
    static int taller = FALSE;  //反映pAT树是否长高
    if(!(*pAT))
    {
        //插入新结点,树长高,taller为TRUE;
        (*pAT) = (AVLTree)malloc(sizeof(AVLNode));
        (*pAT)->cData = c;
        (*pAT)->LChild = (*pAT)->RChild = NULL;
        (*pAT)->nBf = EH;
        taller = TRUE;
    }
    else
    {
        if((*pAT)->cData == c)
        {
            //树中已存在和e相同关键字的结点,不插入
            taller = FALSE;
            return 0;
        }
        if(c < (*pAT)->cData)
        {
            //应该在*pAT的左子树中进行搜索
            if(!InsertATNode(&(*pAT)->LChild, c))   //未插入
                return 0;
            if(taller)  //已插入到树pAT并且左子树长高
            {
                switch((*pAT)->nBf) //检查*pAT的平衡因子
                {
                    case LH:    //原本左子树比右子树高,作左平衡处理
                        LeftBalance(pAT);
                        taller = FALSE;
                        break;
                    case EH:    //原本左右子树等高,现左子树比右子树高1
                        (*pAT)->nBf = LH;
                        taller = TRUE;
                        break;
                    case RH:    //原本右子树比左子树高,现左右子树等高
                        (*pAT)->nBf = EH;
                        taller = FALSE;
                        break;
                }
            }
        }
        else
        {
            //应该在*pAT的右子树中进行搜索
            if(!InsertATNode(&(*pAT)->RChild, c))   //未插入
                return 0;
            if(taller)  //已插入到树pAT并且右子树长高
            {
                switch((*pAT)->nBf) //检查*pAT的平衡因子
                {
                    case LH:    //原本左子树比右子树高,现左右子树等高
                        (*pAT)->nBf = EH;
                        taller = FALSE;
                        break;
                    case EH:    //原本左右子树等高,现右子树比左子树高1
                        (*pAT)->nBf = RH;
                        taller = TRUE;
                        break;
                    case RH:    //原本右子树比左子树高,作右平衡处理
                        RightBalance(pAT);
                        taller = FALSE;
                        break;
                }
            }
        }
    }
    return 1;
}
BOOL DeleteNode(AVLTree *pAT, DataType c)
{
    //若在平衡的二叉树pAT中存在和c相同的关键字结点,
    //则删除一个以c为数据元素的结点,并返回1,否则返回0
    //若因删除而使二叉排序树失去平衡,则作平衡旋转处理
    static int lower = FALSE;   //反映pAT树是否变矮
    if(!(*pAT))     //树为空或结点不存在返回0
        return 0;
    if(c == (*pAT)->cData)
    {
        //存在要删除的结点
        //查找用作替换的结点
        AVLTree Min = FindMin((*pAT)->RChild);
        if(Min != NULL) //存在右子树
        {
            //找到可以用作替换的点
            (*pAT)->cData = Min->cData;
            if(Min != (*pAT)->RChild)
            {
                AVLTree Parent = GetParent((*pAT)->RChild, Min->cData);
                Parent->LChild = Min->RChild;
            }
            else
            {
                (*pAT)->RChild = Min->RChild;
            }

free(Min);
        }
        else //不存在右子树
        {
            //找不到删除的结点
            Min = *pAT;
            *pAT = (*pAT)->LChild;
            free(Min);
        }
        lower = TRUE;
    }
    else if(c < (*pAT)->cData)
    {
        //应该在*pAT的左子树中进行搜索
        if(!DeleteNode(&(*pAT)->LChild, c)) //未删除
            return 0;

if(lower)   //树变矮
        {
            switch((*pAT)->nBf)
            {
                case LH:    //原本左子树比右子树高,现在等高
                    (*pAT)->nBf = EH;
                    lower = TRUE;
                    break;
                case EH:    //原本左右子树等高,现右子树比左子树高1
                    (*pAT)->nBf = RH;
                    lower = FALSE;
                    break;
                case RH:    //原本右子树比左子树高,则作右平衡处理
                    RightBalance(pAT);
                    lower = TRUE;
                    break;
            }
        }

}
    else if(c > (*pAT)->cData)
    {
        //应该在*pAT的右子树中进行搜索
        if(!DeleteNode(&(*pAT)->RChild, c))
            return 0;
        if(lower)
        {
            switch((*pAT)->nBf)
            {
                case LH:    //原本左子树比右子树高,则作左平衡处理
                    LeftBalance(pAT);
                    lower = TRUE;
                    break;
                case EH:    //原本左右子树等高,现左子树比右子树高1
                    (*pAT)->nBf = LH;
                    lower = FALSE;
                    break;
                case RH:    //原本右子树比左子树高,现左左子树等高
                    (*pAT)->nBf = EH;
                    lower = TRUE;
                    break;
            }
        }
    }

return 1;
}
AVLTree FindMin(AVLTree AT)
{
    //查找AT中最小的元素
    while(AT && AT->LChild)
    {
        AT = AT->LChild;
    }
    return AT;
}
AVLTree GetParent(AVLTree AT, DataType c)
{
    if(!AT || AT->cData == c)
        return NULL;
    if((AT->LChild && AT->LChild->cData == c) ||
            (AT->RChild && AT->RChild->cData == c))
        return AT;
    else if((AT->LChild && c < AT->LChild->cData))
        return GetParent(AT->LChild, c);
    else
        return GetParent(AT->RChild, c);
}

AVLTree FindATNode(AVLTree AT, DataType c)
{
    if(!AT)
        return NULL;
    else if(AT->cData == c)
        return AT;
    else if(c < AT->cData)
    {
        return FindATNode(AT->LChild, c);
    }
    else
    {
        return FindATNode(AT->RChild, c);
    }
}

现在对这种实现方法作一点小小的分析。

1、至于时间复杂度的分析就不必多说了,所有的算法的时间复杂度都为log2N。

2、从代码的数量可以看出这并不是一种好的实现方法,因为它的思路不太清晰和直观,操作实现比较复杂,还要用到二重指针,增加了程序出错的机会。

3、导致复杂的原因主要有两个,

1)第一个就是在高度信息储存方法上,不是采用每个结点保存自己作为一棵树的深度,而是保存着左右子树之差(左子树的深度 - 右子树的深度),从而产生了大量的判断和switch语句,让人眼花瞭乱,并且影响程序的效率和易读性,难以维护,所以用一个变量记录树的高度会让程序思路更清晰。

2)再者,在这里的旋转、插入的删除操作都要调节结点并改变指向结点的指针变量的值,在C语言中是没有引用的(C++有),所以就要用到双重指针,这无疑加大了程序的复杂度,而且使程序更容易出错。而避免这样的情况的做法是很简单的,只需要做一个简单的修改。就是让旋转插入等操作的返回值为指向结点的指针,而不是一个标志操作是否成功的状态值。而且这样做还有一个好处,就是通常最后插入的数据总是最先被访问,这样就可以根据返回的结点的指针马上访问该结点,而不用再在整棵树中查找。

算法如有错误,还请各位读者指出,谢谢!

改进后的算法会在下一篇的博客中给出。

AVL树的插入删除查找算法实现和分析-1的更多相关文章

  1. AVL 树的插入、删除、旋转归纳

    参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339   1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根 ...

  2. AVL树的插入操作(旋转)图解

    =================================================================== AVL树的概念       在说AVL树的概念之前,我们需要清楚 ...

  3. AVL树的插入与删除

    AVL 树要在插入和删除结点后保持平衡,旋转操作必不可少.关键是理解什么时候应该左旋.右旋和双旋.在Youtube上看到一位老师的视频对这个概念讲解得非常清楚,再结合算法书和网络的博文,记录如下. 1 ...

  4. 数据结构系列之2-3树的插入、查找、删除和遍历完整版代码实现(dart语言实现)

    弄懂了二叉树以后,再来看2-3树.网上.书上看了一堆文章和讲解,大部分是概念,很少有代码实现,尤其是删除操作的代码实现.当然,因为2-3树的特性,插入和删除都是比较复杂的,因此经过思考,独创了删除时分 ...

  5. 数据结构系列之2-3-4树的插入、查找、删除和遍历完整版源代码实现与分析(dart语言实现)

    本文属于原创,转载请注明来源. 在上一篇博文中,详细介绍了2-3树的操作(具体地址:https://www.cnblogs.com/outerspace/p/10861488.html),那么对于更多 ...

  6. DS-二叉排序树的插入、查找和删除

    2019-12-02(菜鸡开始学习了...) Data Structure 之 二叉排序树 二叉排序树是给定一个节点后,接下来插入的数如果比它大就会放到它的右孩子那边,比它小就会放到它的左孩子那边. ...

  7. 红黑树和AVL树的实现与比较-----算法导论

    一.问题描述 实现3种树中的两种:红黑树,AVL树,Treap树 二.算法原理 (1)红黑树 红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是red或black.红黑树满足以 ...

  8. 二叉搜索树-php实现 插入删除查找等操作

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的 ...

  9. 树——平衡二叉树插入和查找的JAVA实现(2):增加删除方法

    package com.tomsnail.data.tree; /** * AVL二叉平衡树 * @author tomsnail * @date 2015年3月30日 下午4:35:50 */ pu ...

随机推荐

  1. 如何把你的图标转换成web字体

    在这篇教程中,我们将使用一个免费的Web应用程序IcoMoon将矢量图转换成Web字体,然后将生成的字体通过css应用到Web页面中. 通常我们在网站中必不可少的会使用到一些小图标.在正常尺寸下,布局 ...

  2. homework-01 &quot;最大子数组之和&quot;的解决过程

    看到这个题目,我首先想到就是暴力解决 求出所有的子数组的和,取出最大值即可 但其中是可以有优化的 如 子数组[3:6]可以用[3:5]+[6]来计算 即可以将前面的计算结果保留下来,减少后面的重复计算 ...

  3. vsftpd配置文件说明

    (1)常用选项: chroot_local_user=YES #限制所有的用户均不能切换到其他目录 allow_writeable_chroot=YES #允许根目录可写 FTP的工作模式有两种,一种 ...

  4. Jquery 强大的表单验证操作

    参考资料: 1.https://www.cnblogs.com/linjiqin/p/3431835.html(此篇最佳) 2.https://blog.csdn.net/pengjunlee/art ...

  5. CentOS 7系统上制作Clonezilla(再生龙)启动U盘并克隆双系统

    笔记本安装的是双系统:Win7 64位,CentOS 7 64位. 政采就是个巨大的坑,笔记本标配的是5400转的机械硬盘,开机时间常常要一至两分钟,软件运行起来时各种数据的读写也非常慢,忍无可忍,决 ...

  6. InnoDB FULLTEXT

    1.概要 InnoDB引擎对FULLTEXT索引的支持是MySQL5.6新引入的特性,之前只有MyISAM引擎支持FULLTEXT索引.对于FULLTEXT索引的内容可以使用MATCH()…AGAIN ...

  7. CHAPTER 7 Science in Islam 第7章 伊斯兰中的科学

    CHAPTER 7 Science in Islam 第7章 伊斯兰中的科学 Galen did not live to see the decline of the Roman Empire, bu ...

  8. 与R纠缠的两件事——rownames和子集--转载

    与R语言纠缠了一个星期,从快速上手的暗暗得意,到之后某些细节的纠结烦躁,过山车式体验中,我逐渐才认识了真实的R语言. 期间遇到两个很烦恼的问题,一个是让人烦躁抓狂,另一个是无意发现的重大错误. 1.  ...

  9. ASP.NET OAuth 2.0 新手上路

    OAuth2.0资料 初衷:一直想整理授权系列demo,让自己项目高端大尚,列出新手授权系列,帮助小白程序员不用在为授权头疼 OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服 ...

  10. 牛B的日本精神

    在汤森路透评选出的<2015全球创新企业百强>榜单里,日本以40家高居榜首,力压美国的35家.而中国内地无一入围.   在中国媒体上,我们见到的日本是“失去的20年”,经济衰退.创新能力丧 ...