AVL树的初步生成与插入操作

      平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法 平衡二叉树的常用算法有红黑树、AVL、Treap等。 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。
      AVL是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
      如何生成一个二叉树呢,我们可以来看看下面这一幅图:

      一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是)离插入结点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行的调整规律有四种:
      (1)**单向右旋平衡处理**;
          使用在插入结点位置为*a的**左子树根结点的左子树**,且树失去平衡。

      (2)**单向左旋平衡处理**;
            使用在插入结点位置为*a的**右子树根结点的右子树**,且树失去平衡。

      (3)**双向旋转(先左后右)平衡处理**;
      使用在插入结点位置为*a的**左子树根结点的右子树**,且树失去平衡。

      (4)**双向旋转(先右后左)平衡处理**;
            使用在插入结点位置为*a的**右子树根结点的左子树**,且树失去平衡。

           下面**有图有真相**

下面显示程序代码:

功能有:

(1)插入数组中的元素生成AVL树。

(2)中序遍历AVL树得到正序数据。

(3)查找是否AVL树中是否存在要求的关键字。

(4)不存在要求的关键字的时候选择是否插入关键字到AVL树中。

(5)中序遍历新的AVL树。

#include<iostream>
#include<stdlib.h>
using namespace std;

//测试元素数量
#define N 5

//定义平衡状态
#define LH 1
#define EH 0
#define RH -1
#define TRUE 1
#define FALSE 0

//比较
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
#define RT(a,b) ((a)>(b))

//数据元素类型定义
struct ElemType
{
    int key;
    int order;
};

//AVL树的定义
typedef struct AVLNode
{
    ElemType data;
    int bf;         //结点的平衡因子
    struct AVLNode *lchild,*rchild;     //左右孩子指针
}AVLNode,*AVLTree;

//下面是AVL树常用操作

//单右旋处理
void R_Rotate(AVLTree &p)
{
    //对以*p为根的二叉排序树做右旋处理,处理后p指向新的树根节点,即旋转处理之前的左子树的根节点
    AVLTree lc;
    lc=p->lchild;           //lc指向*p的左子树根节点
    p->lchild=lc->rchild;   //lc的右子树挂接为*p的左子树
    lc->rchild=p;
    p=lc;  //p指向新的根结点
}

//单左旋处理
void L_Rotate(AVLTree &p)
{
    //对以*p为根的二叉排序树做左旋处理,处理后p指向新的树根节点,即旋转处理之前的右子树的根节点
    AVLTree rc;
    rc=p->rchild;           //rc指向*p的右子树根节点
    p->rchild=rc->lchild;   //lc的左子树挂接为*p的右子树
    rc->lchild=p;
    p=rc;  //p指向新的根结点
}

//左平衡旋转处理
void LeftBalance (AVLTree &T)
{
    //对以指针T所指结点为根的二叉树做左平衡旋转处理,算法结束后,指针T指向新的根节点
    AVLTree lc,rd;
    lc=T->lchild;           //lc指向*T的左子树根节点
    switch (lc->bf)
    {
    case LH:                //新结点插入在*T的左孩子的左子树上,要做单右旋处理。
        T->bf=lc->bf=EH;
        R_Rotate(T);break;
    case RH:                //新结点插入在*T的左孩子的右子树上,要做双旋处理
        rd=lc->rchild;      //rd指向*T的左孩子的右子树根
        switch (rd->bf)
        {
            //修改*T及其左孩子的平衡因子
        case LH:
            T->bf=RH;
            lc->bf=EH;
            break;
        case EH:
            T->bf=lc->bf=EH;
            break;
        case RH:
            T->bf=EH;
            lc->bf=LH;
            break;

        default:
            break;
        }
        //rd->bf=EH;
        L_Rotate(T->lchild);    //  对*T的左子树做左旋平衡处理
        R_Rotate(T);            //  对*T做右旋平衡处理

    default:
        break;
    }

}

//右平衡旋转处理
void RightBalance (AVLTree &T)
{
    //对以指针T所指结点为根的二叉树做右平衡旋转处理,算法结束后,指针T指向新的根节点
    AVLTree rc,rd;
    rc=T->rchild;           //rc指向*T的右子树根节点
    switch (rc->bf)
    {
    case RH:                //新结点插入在*T的右孩子的右子树上,要做单左旋处理。
        T->bf=rc->bf=EH;
        L_Rotate(T);break;
    case LH:                //新结点插入在*T的右孩子的左子树上,要做双旋处理
        rd=rc->lchild;      //rd指向*T的右孩子的左子树根
        switch (rd->bf)
        {
            //修改*T及其右孩子的平衡因子
        case LH:
            T->bf=EH;
            rc->bf=RH;
            break;
        case EH:
            T->bf=rc->bf=EH;
            break;
        case RH:
            T->bf=LH;
            rc->bf=EH;
            break;

        default:
            break;
        }
        rd->bf=EH;
        R_Rotate(T->rchild);    //对*T的右子树做右旋平衡处理
        L_Rotate(T);            //  对*T做左旋平衡处理
    default:
        break;
    }

}

//插入操作
int InsertAVL(AVLTree &T,ElemType e,bool &taller)
{
    //若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个数据元素
    //为e的新结点,并返回1,否则返回0.若因插入而使二叉排序树失去平衡,则做平衡二叉树
    //旋转处理,布尔变量taller反映T长高与否

    if(!T)
    {
        //插入新结点,树长高,置taller为TRUE
        T=(AVLTree)malloc(sizeof(AVLNode));
        T->data=e;
        T->lchild=T->rchild=NULL;
        T->bf=EH;
        taller=TRUE;
    }
    else
    {
        if(EQ(e.key,T->data.key ))      //树中已经存在和e相同关键字的结点
        {
            taller=FALSE;
            return 0;                   //跳出程序,不再插入
        }
        else if(LT (e.key,T->data.key))  //在左子树寻找
        {
            if(!InsertAVL(T->lchild,e,taller))      return 0;   //递归调用插入函数,无插入则跳出
            if(taller)                  //  已插入到*T的左子树中且左子树“增高”
                switch(T->bf)           //  检查*T的平衡度
                {
                case LH:                //  原本左子树比右子树高,需要做左平衡处理
                    LeftBalance(T);taller =FALSE; break;
                case EH:                //  原本左右子树等高,现在因为左子树增高而使树增高
                    T->bf=LH; taller=TRUE;break;
                case RH:                //  原本右子树比左子树高,现在左右子树等高
                    T->bf=EH;
                    taller=FALSE;
                }
        }
        else
        {
            if(!InsertAVL(T->rchild,e,taller))      return 0;   //递归调用插入函数
            if(taller)              //  已插入到*T的左子树中且左子树“增高”
                switch(T->bf)       // 检查*T的平衡度
                {
                case LH:            //  原本左子树比右子树高,现在左右子树等高
                    T->bf=EH;
                    taller=FALSE;
                case EH:            //  原本左右子树等高,现在因为右子树增高而使树增高
                    T->bf=RH; taller=TRUE;break;
                case RH:            //  原本右子树比左子树高,现在做右平衡处理
                    RightBalance(T);taller =FALSE; break;
                }
        }
    }
    return TRUE;
}
//visit 函数
void visit(ElemType e)
{
    cout<<"( "<< e.key<<", "<<e.order<<" )   ";
}

//中序遍历二叉树
void InOrderTraverse(AVLTree T,void(*visit)(ElemType e))
{
    //二叉树存在;visit函数是对结点进行操作
    //对二叉树进行先序遍历,每一个结点有且只有一次调用visit
    if(T)                                           //树非空
    {
        InOrderTraverse(T->lchild,visit);           //对左子树进行遍历
        visit(T->data);
        InOrderTraverse(T->rchild,visit);           //对右子树进行遍历
    }

}
//查找关键字
int searchAVL(AVLTree &T,int key)
{
    //AVLTree p,f;
    if(!T)
    {
        char i;
        bool k;
        cout<<"没有此关键字"<<endl;
        cout<<"是否需要插入此关键字进AVL?(Y/N)"<<endl;
        cin>>i;
        if(i==89||i==121)
        {
            ElemType temp;
            temp.key=key;
            temp.order=N+1;
            InsertAVL(T,temp,k);
        }

        return FALSE;
    }
    else if(EQ(key,T->data.key))
        cout<<"( "<< T->data.key<<", "<<T->data.order<<" )   "<<endl;
    else if(LT(key,T->data.key))
        searchAVL(T->lchild,key);
    else
        searchAVL(T->rchild,key);
}
//初始化空树(构造空树)
void InitAVL(AVLTree &T)
{
    T=NULL;
}

int main()
{
    AVLTree at;
    int i,j;
    bool k;
    ElemType e[N]={{13,1},{24,2},{37,3},{90,4},{53,5}};
    InitAVL(at);
    for(i=0;i<N;i++)
    {
        InsertAVL(at,e[i],k);
    }
    InOrderTraverse(at,visit);
    //PreOrderTraverse(at,visit);
    cout<<endl;
    cout<<"请输入要寻找的关键字"<<endl;
    cin>>j;
    searchAVL(at,j);
    cout<<"重新中序遍历"<<endl;
    InOrderTraverse(at,visit);
    cout<<endl;
    system("pause");
    return 0;
}

结果截图

这里代码参考的是上面一幅图——平衡树生成过程的数组。

ps:这里只是讲到AVL树的生成插入过程,删除操作下回细说。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 21:16:03

AVL树的初步生成与插入操作的相关文章

平衡二叉查找树——AVL树

二叉查找树在最坏情况下高度可能为N-1,即插入元素时后插入的元素总比以前插入的元素大或者小.为了解决这种不平衡的情况,引入了平衡条件来限制树中节点的深度不能过深,其中最老的一种平衡树称为AVL树.这种树限制树中每个节点的左右子树的高度相差不能超过一.(另一种更严格的树限制节点的左右子树高度必须相等,但这样的树要求树中的节点数目为2的k次幂减1,是一种理想平衡树,但是要求太严格,无法实际使用.) AVL树平衡条件分析 AVL树是一棵特殊的二叉查找树,对AVL树的操作中,除了插入操作与普通二叉查找树

平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

一.AVL树(平衡二叉搜索树)是什么? AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的.AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个非叶子结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5             5 / \            /  \ 2   6         2   6 / \    \         / \ 1  4   7       1  4

[转载]AVL树(一)之 图文解析 和 C语言的实现

概要 本章介绍AVL树.和前面介绍"二叉查找树"的流程一样,本章先对AVL树的理论知识进行简单介绍,然后给出C语言的实现.本篇实现的二叉查找树是C语言版的,后面章节再分别给出C++和Java版本的实现. 建议:若您对"二叉查找树"不熟悉,建议先学完"二叉查找树"再来学习AVL树. 目录 1. AVL树的介绍 2. AVL树的C实现3. AVL树的C实现(完整源码) 4. AVL树的C测试程序 转载请注明出处:http://www.cnblogs.

Linux内核之于红黑树and AVL树

为什么Linux早先使用AVL树而后来倾向于红黑树?       实际上这是由红黑树的实用主义特质导致的结果,本短文依然是形而上的观点.红黑树可以直接由2-3树导出,我们可以不再提红黑树,而只提2-3树,因为 2-3树的操作太简单.另外,任何红黑树的操作和特性都可以映射到2-3树中.因此红黑树和AVL树的比较就成了2-3树和AVL树的比较. 它们俩的区别在哪?2-3树的平衡是完美平衡的,但是树杈数量却可以是3个,而AVL树差一点点就完美平衡的标准二叉树,它只允许子树的高度差最多为1. 可见这么看

算法学习笔记 平衡二叉树 AVL树

AVL树是最先发明的自平衡二叉查找树, 其增删查时间复杂度都是 O(logn), 是一种相当高效的数据结构.当面对需要频繁查找又经常增删这种情景时,AVL树就非常的适用.[ 博客地址:http://blog.csdn.net/thisinnocence ] AVL树定义 AVL树诞生于 1962 年,由 G.M. Adelson-Velsky 和 E.M. Landis 发明.AVL树首先是一种二叉查找树.二叉查找树是这么定义的,为空或具有以下性质: 若它的左子树不空,则左子树上所有的点的值均小

AVL树及其C语言实现

1.AVL树简介 AVL树是带有平衡条件的二叉查找树,这个平衡条件必须容易保持.前面我写过二叉搜索树,然而这个树的最大深度为n,最小深度为logn,因此查找时效率不是特别高,我们可以构建这样一棵树,它的最大深度始终为logn,这就是AVL树,它要求每个节点的左子树和右子树的高度最多差1的二叉查找树.(空树高度定义为-1) AVL树的平衡必须在每一步操作中都可以保持,在插入以后,只有那些从插入点到根节点的路径上的节点的平衡条件可能会被改变,因为只有这些节点的子树可能发生变化.当我们沿着这条路径上行

AVL树的JAVA实现及AVL树的旋转算法

1,AVL树又称平衡二叉树,它首先是一颗二叉查找树,但在二叉查找树中,某个结点的左右子树高度之差的绝对值可能会超过1,称之为不平衡.而在平衡二叉树中,任何结点的左右子树高度之差的绝对值会小于等于 1. 2,为什么需要AVL树呢?在二叉查找树中最坏情况下查找某个元素的时间复杂度为O(n),而AVL树能保证查找操作的时间复杂度总为O(logn). 3,AVL树的JAVA代码实现: AVLTree  继承 BinarySearchTree 并改写 添加节点的add方法,在add方法中判断插入元素后是否

从AVL树的定义出发,一步步推导出旋转的方案。

本文从AVL树的定义出发,一步步地推导出AVL树旋转的方案,这个推导是在已经清楚地知道AVL树的定义这个前提下进行的.文章注重思考的过程,并不会直接给出AVL树是怎样旋转的,用来提醒自己以后在学习的时候要注重推导的过程.在此,我要特别感谢下我的数据结构老师,是他让我意识到思考的重要性. 一.从AVL树的定义开始 1. 二叉查找树的问题 二叉查找树的出现,虽然使查找的平均时间降到了logN,但是,在多次删除或者插入操作后,可能会出现根节点的左子树比右子树高很多,或者右子树比左子树高很多的情况.如图

深度解析(七)AVL树

AVL树(一)之 图文解析 和 C语言的实现 概要 本章介绍AVL树.和前面介绍"二叉查找树"的流程一样,本章先对AVL树的理论知识进行简单介绍,然后给出C语言的实现.本篇实现的二叉查找树是C语言版的,后面章节再分别给出C++和Java版本的实现.建议:若您对"二叉查找树"不熟悉,建议先学完"二叉查找树"再来学习AVL树. 目录 1. AVL树的介绍2. AVL树的C实现3. AVL树的C实现(完整源码)4. AVL树的C测试程序 转载请注明出处