平衡二叉树(AVL树)的实现

一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1)。AVL树是对二叉查找树的一种改进,虽然二叉查找树平均情况下比链表的查找速度要快,但是二叉查找树也可能出现所有结点都没有左孩子(或右孩子)的情况,此时树的高度为n,这就和链表可以等价为一种数据结构,为了避免这种情况的发生,AVL树就应运而生。

第一步:结点信息

与二叉搜索树的结点信息类似,只需要添加一个树高的参数,用于插入和删除操作过程的旋转。

struct AVLnode
    {
        T data;//结点的元素值
        AVLnode *left;//指向左孩子
        AVLnode *right;//指向右孩子
        int height;//树高
        //构造函数
        AVLnode(T elem, AVLnode *l, AVLnode *r, int h=0):data(elem),left(l),right(r),height(h){}
    };

第二步:平衡二叉树类的声明

主要包括树的插入删除,以及保持树平衡的各种旋转操作。

public://对外的接口函数
    AVLTree():root(NULL){}

    void insert(const T &x)//结点插入
    {
        insert(x, root);
    }
    void remove(const T &x)//结点删除
    {
        remove(x, root);
    }
private:
    AVLnode *root;//根结点
    //结点插入和删除
    void insert(const T &x, AVLnode * &t);
    void remove(const T &x, AVLnode * &t);
    //树的旋转操作
    void SingleRotateLeft(AVLnode * &t);
    void SingleRotateRight(AVLnode * &t);
    void DoubleRotateLR(AVLnode * &t);
    void DoubleRotateRL(AVLnode * &t);

第三步:辅助函数

辅助函数有四个:两个用于旋转操作,一个是求树高,一个是求两个值的最大值;两个是用于删除操作,分别是求某一子树的最大值和最小值的结点。

//辅助函数
    int height(AVLnode * &t)
    {
        //结点高度,空结点的高度为-1
        return t==NULL?-1:t->height;
    }
    int max(int a, int b)
    {
        return a<b?b:a;
    }

    AVLnode * max_node(AVLnode *t)
    {
        if(t)
            while(t->right)
                t = t->right;
        return pCur;
    }

    AVLTree * min_node(AVLnode *t)
    {
        if(t)
            while(t->left)
                t = t->left;
        return t;
    }

第四步:旋转

如果把必须重新平衡的结点叫k。由于任意结点最多有两个孩子,因此高度不平衡时,k点的两棵子树的高度差为2。容易看出,这种不平衡可能出现在下面四种情况中:

1)对k的左孩子的左子树进行一次插入。

2)对k的左孩子的右子树进行一次插入。

3)对k的右孩子的左子树进行一次插入。

4)对k的右孩子的右子树进行一次插入。

情形1和4是关于k点的镜像对称,而2和3是关于k点的镜像对称。因此,理论上只有两种情况,当然从编程的角度来看还是四种情况。

单旋转

对于情形1)和情形4),只需要进行一次旋转就可以恢复AVL树的平衡,以情形1)为例,阐述单旋转操作:

一次旋转就能恢复AVL树的平衡,不过还需要调整相应结点的height属性,并将结点s置为当前结点。这种旋转操作,就好像人为的把s结点向右旋转一次,所以该操作也叫做右旋转

左孩子的左子树插入结点失衡——右旋转

右孩子的右子树插入结点失衡——左旋转

单旋转实现代码如下所示:

template <typename T>
void AVLTree<T>::SingleRotateLeft(AVLnode * &t)
{
    AVLnode *s = t->right;
    t->right = s->left
    s->left = t;
    t->height = max(height(t->left), height(t->right)) + 1;
    s->height = max(t->height, height(s->right)) + 1;
    t = s;
}

template <typename T>
void AVLTree<T>::SingleRotateRight(AVLnode * &t)
{
    AVLnode *s = t->left;
    t->left = s->right;
    s->right = t;
    t->height = max(height(t->left), height(t->right)) + 1;
    s->height = max(height(s->left), t->height);
    t = s;
}

双旋转

情形2)和3)稍微复杂一点,需要进行两次旋转才能恢复AVL树的平衡。以情形2)为例,阐述双旋转操作:

同样,旋转操作之后,还要修改结点的height属性值,并将q设置为当前结点。从上图可以看出,双旋转实际上就是两次简单的单旋转,所以上述操作可以通过两次单旋转函数的调用来实现

t->left = SingleRotateLeft(t->left);
return SingleRotateRight(t);

左孩子的右子树插入结点导致不平衡——先左旋转,再右旋转

右孩子的左子树插入结点导致不平衡——先右旋转,再左旋转

双旋转的代码实现如下所示:

template <typename T>
void AVLTree<T>::DoubleRotateLR(AVLnode* &t)
{
    AVLnode *p = t->left;
    AVLnode *q = p->right;
    t->left = q->right;
    p->right = q->left;
    q->left = p;
    q->right = t;
    t->height = max(height(t->left), height(t->right)) + 1;
    p->height = max(height(p->left), height(p->right)) + 1;
    q->height = max(p->height, t->height) + 1;
    t = q;
}

template <typename T>
void AVLTree<T>::DoubleRotateRL(AVLnode* &t)
{
    AVLnode *p = t->right;
    AVLnode *q = p->left;
    t->right = q->left;
    p->left = q->right;
    q->left = t;
    q->right = p;
    t->height = max(height(t->left), height(t->right)) + 1;
    p->height = max(height(p->left), height(p->right)) + 1;
    q->height = max(t->height, p->height) + 1;
    t = q;
}

第五步:插入和删除操作

插入和删除操作与普通二叉搜索树一样,只是需要在插入和删除操作之后,为了保持树的平衡性,进行相应的旋转操作。

插入和删除操作的代码实现如下所示:

template <typename T>
void AVLTree<T>::insert(const T &x, AVLnode * &t)
{
    if(!t)//找到插入点
        t = new AVLnode(x, NULL, NULL);
    else if(x < t->data)
    {
        insert(x, t->left);
        if(height(t->left) - height(t->right) == 2)
        {
            if(x < t->left->data)//结点插在当前结点左孩子的左子树
                SingleRotateRight(t);//右单旋转
            else//结点插在当前结点左孩子的右子树
                DoubleRotateLR(t);//先左旋再右旋
        }
        else//不需要调整,只需要重新计算树的高度
            t->height = max(height(t->left), height(t->right)) + 1;
    }
    else if(x > t->data)
    {
        insert(x, t->right);
        if(height(t->right) - height(t->left) == 2)
        {
            if(x > t->right->data)//结点插在当前结点右孩子的右子树
                SingleRotateLeft(t);//左单旋转
            else//结点插在当前结点左孩子的右子树
                DoubleRotateRL(t);//先右旋再左旋
        }
        else//不需要调整,只需要重新计算树的高度
            t->height = max(height(t->left), height(t->right)) + 1;
    }
    else;//不考虑重复的值
}

template <typename T>
void AVLTree<T>::remove(const T &x, AVLnode * &t)
{
    if(!t)return;//找到叶结点以下,即没有要删除的结点,啥也不做
    if(x < t->data)
    {
        remove(x, t->left);
        if(height(t->right) - height(t->left) == 2)
        {
            AVLnode * temp = t->right;
            if(height(temp->right) > height(temp->left))//类似于结点插在当前结点右孩子的右子树上
                SingleRotateLeft(t);//左单旋转
            else//类似结点插在当前结点右孩子的左子树
                DoubleRotateRL(t);//先右旋再左旋
        }
        else//不需要调整,只需要重新计算树的高度
            t->height = max(height(t->left), height(t->right)) + 1;
    }
    else if(x > t->data)
    {
        remove(x, t->right);
        if(height(t->left) - height(t->right) == 2)
        {
            AVLnode * temp = t->left;
            if(height(temp->left) > height(temp->right))//类似于结点插在当前结点左孩子的左子树上
                SingleRotateRight(t);//右单旋转
            else//类似结点插在当前结点左孩子的右子树
                DoubleRotateLR(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))
            {
                t->data = max_node(t->left)->data;
                remove(t->data, t->left);
            }

            else
            {
                t->data = min_node(t->right)->data;
                remove(t->data, t->right);
            }
        }
        else
        {
             AVLnode *oldnode = t;
             t = t->left?t->left:t->right;
             delete oldnode;
             oldnode = NULL;
        }
    }
}



平衡二叉树(AVL树)的实现

时间: 2024-10-10 01:32:53

平衡二叉树(AVL树)的实现的相关文章

图解平衡二叉树,AVL树(一)

图解平衡二叉树,AVL树(一) 学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点. 在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前

平衡二叉树,AVL树之图解篇

学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点.            在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前苏联的数学

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

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

java数据结构与算法之平衡二叉树(AVL树)的设计与实现

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/53892797 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) java数据结构与算法之栈(Stack)设

图解:平衡二叉树,AVL树

学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点.            在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前苏联的数学

平衡二叉树-AVL树的构建和操作

首先上一下单纯的二叉树插入节点的代码,用的是不返回新根节点的方式: 1 void insertNode(BTree *&root,int data) 2 { 3 if (root == nullptr)//当根节点为NULL时,在根节点上插入 4 { 5 root = initRoot(data); 6 return; 7 } 8 if (root->data == data)//当插入数据等于根节点时,不插入 9 { 10 return; 11 } 12 if (root->data

平衡二叉树,AVL树之代码篇

看完了第一篇博客,相信大家对于平衡二叉树的插入调整以及删除调整已经有了一定的了解,下面,我们开始介绍代码部分. 首先,再次提一下使用的结构定义 1 typedef char KeyType; //关键字 2 typedef struct MyRcdType //记录 3 { 4 KeyType key; 5 }RcdType,*RcdArr; 6 typedef enum MyBFStatus //为了方便平衡因子的赋值,这里进行枚举 7 { //RH,EH,LH分别表示右子树较高,左右子树等高

大话数据结构—平衡二叉树(AVL树)

平衡二叉树(Self-Balancing Binary Search Tree/Height-Balanced Binary Search Tree),是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1. 平衡二叉树的前提是二叉排序树,不是二叉排序树的都不是平衡二叉树. 平衡因子BF(Balance Factor):二叉树上节点的左子树深度减去右子树深度的值. 最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树. 下图中,新插入节点37时,距离它最近的平

【数据结构】第9章 查找! (二叉搜索树BST AVL树 B-(+)树 字典树 HASH表)

难产的笔记...本来打算用1天 结果前前后后拖了5天 §9.1 静态查找表 9.1.1 顺序表的查找 各种扫 自己脑补吧 复杂度O(n) 9.1.2 有序表的查找 若表是单调的,则可以利用二分查找.复杂度O(logn) 9.1.3 静态树表的查找 见 http://blog.csdn.net/area_52/article/details/43795837 9.1.4 索引顺序表的查找 建立索引表查找 §9.2 动态查找表 动态查找表的特点是,表结构本身是在查找过程中动态生成的,即对于给定值ke

数据结构与算法系列----平衡二叉树(AVL树)

一:背景 平衡二叉树(又称AVL树)是二叉查找树的一个进化体,由于二叉查找树不是严格的O(logN),所以引入一个具有平衡概念的二叉树,它的查找速度是O(logN).所以在学习平衡二叉树之前,读者需要了解二叉查找树的实现,具体链接:二叉查找树 那么平衡是什么意思?我们要求对于一棵二叉查找树 ,它的每一个节点的左右子树高度之差不超过1.(对于树的高度的约定:空节点高度是0:叶子节点高度是1.)例如下图: 如果我们的二叉查找树是不平衡该怎么办?进行旋转.经过分析发现,出现不平衡无外乎四种情况,下面我