至于什么是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树是最早被发明的自平衡二叉查找树.在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度 ...

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

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

  4. AVL树的插入与删除

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 省部级干部list

    省部级干部表 省(市.自治区) 省(市)委书记 省(市)人大主任 省(市)长 省(市)政协主席 北京市 郭金龙 杜德印 王安顺 吉林 天津市 黄兴国 肖怀远 黄兴国 臧献甫 上海市 韩正 殷一璀(女) ...

  2. HDU 4720 Naive and Silly Muggles (外切圆心)

    Naive and Silly Muggles Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...

  3. CoreAnimation —— CALayer

    概述 如上篇博文讲述,UIView中封装了很多系统方法,可以满足我们的大部分需求.但是,其也有很多限制.那些方法产生的动画基本单元为UIView,是非常重量级的对象,而且也不支持三维布局,大部分是对视 ...

  4. vim下设置tab

    前言:大多数情况下tab键的宽度设置为4个空格,这个可以根据自己 的代码风格进行替换,然而当你提交不同的语言的代码的时候python 和c的时候就有区别了.c的话一般tab键做缩进,而python提交 ...

  5. Android:ListViewAdapter

    MainActivity: package com.wyl.listview; import java.util.ArrayList; import java.util.HashMap; import ...

  6. CentOS Linux使用crontab运行定时任务详解

    参考博文: (总结)CentOS Linux使用crontab运行定时任务详解

  7. 我的Python成长之路---GitHub使用克隆GitHub(SSH key配置)

    六.克隆GitHub仓库 1.创建仓库目录,目录位置没有要求,比如D:\learngit. 2.配置ssh(如果不配置会每次都输入用户名和密码) 使用TortoiseGit生成ssh-key:开始菜单 ...

  8. ubuntu 安装python,easy_install和pip

    ubuntu12.04默认安装的python为 ms@ubuntums:~$ pythonPython 2.7.3 (default, Aug 1 2012, 05:16:07) 我需要用python ...

  9. Citrix XenApp6.5 另类发布文档

    快捷方式.文档发布 第一种方式: 1.    创建快捷方式 2.    创建批处理文件(例如:lnk.bat),并输入以下内容: Start c:\users\public\desktop\adobe ...

  10. Tri_integral Summer Training 8 总结

    比赛链接 题目 B C E F G I 这是孟加拉国的区域赛. 开场ss读懂了c发现是个水题,于是去敲,结果手贱wa了一炮,不过很快就修正了错误.B题过了不少,我去读,发现是个水题,意识让Moor敲. ...