平衡二叉树的插入和删除

 

转载:https://blog.csdn.net/weixin_36194037/article/details/79440464

转载:https://www.cnblogs.com/suimeng/p/4560056.html

在学习二叉排序树的查找时,通过分析查找算法的效率可知,不同结构的二叉排序树查找效率有很大的不同,单支树(图1)的查找效率相当于顺序查找,而越趋于平衡的二叉排序树(图2)查找效率越高。因此,在二叉排序树的基础上引进了平衡二叉树。

概念

平衡二叉树的目的是:

  • 为了防止二叉排序树的最后坏的情况(当先后插入的关键字有序时,构成的二叉排序树蜕变为单支树,树的深度为其平均查找长度(n+1)/2(和顺序查找相同),时间复杂度为O(n))。
  • 也就是将二叉排序树处于最好的情况(二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log 2 (n)成正比。算法时间复杂度为O(1))

平衡二叉树或者是棵空树,或者是具体下列性质的二叉查找树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度只差的绝对值不超过1。

若将二叉树结点的平衡因子定义为该节点的左子树的高度减去它的右子树的高度,则所有结点的平衡因子只可能为-1,0,1。只要有一个结点的平衡因子的绝对值大于1,那么这棵树就失去了平衡(插入和删除都会导致失衡),此时需要重新旋转最小失衡子树,将树重新变为平衡二叉树。

AVL树的插入步骤:

(1)递归找到插入的位置,生成一个新的节点插入到树中。

(2)从当前的位置开始往上查找(也就是递归返回到上一个查找的节点),更新节点的高度,判断节点是否平衡;

(3)不平衡,找到最小不平衡节点,也就找到了最小失衡子树。对最小失衡子树进行旋转,变成平衡二叉树。

(3)平衡,继续(2)步骤,直到判断完所有插入过程中经过的节点,递归结束。

//递归查找插入的位置,t是根节点、父节点、或者是最小不平衡节点,x是插入的值
template <typename T>
void AvlTree<T>::Insert(AvlNode<T> *&t, T x)
{
    if (t == NULL)//当节点为空时插入一个节点
        t = new AvlNode<T>(x);
    else if (x < t->data) //向左遍历
    {
        Insert(t->left, x);//向左递归,直到左节点为空节点,那么就生成一个新的节点作为左节点
        
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {//判断平衡情况,左节点的深度-右节点的深度=平衡因子>1,表示当前节点不平衡,也就是当前节点是 最小不平衡节点
            //分两种失衡情况 左左或左右 
            if (x < t->left->data)//左左,//插入的节点小于最小不平衡节点的左节点的值,也就是说插入的节点在最小不平衡节点的左节点的左边,右旋转    
                t = LL(t);
            else                  //左右,也就是说插入的节点在最小不平衡节点的左节点的右边
                t = LR(t);
        }
    }
    else if (x > t->data) //向右遍历
    {
        Insert(t->right, x);//向右递归,zhi
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {//找到最小不平衡节点,也就是最小失衡子树
            if (x > t->right->data)//右右,插入的值大于最小不平衡节点的右节点,也就是插入的值在最小不平衡节点的右节点的右边
                t = RR(t);
            else
                t = RL(t);
        }
    }
    else
        ;//数据重复
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;//插入值路径上的所有节点高度都发生变化。计算高度 = 左右高度的最大高度+1;
}

AVL树的删除步骤:

同插入操作一样,删除结点时也有可能破坏平衡性,这就要求我们删除的时候要进行平衡性调整。

删除分为以下几种情况:

首先在整个二叉树中搜索要删除的结点,如果没搜索到直接返回不作处理,否则执行以下操作:

1.如果要删除的节点是当前根节点T。(当前根节点T,表示递归中的当前节点,而不是整个树的根节点

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

如果左右子树都非空。在高度较大的子树中实施删除操作。

分两种情况:

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

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

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

(1)那么继续进行递归调用,直到在左子树中找到删除节点,找到删除节点后,执行1步骤。

(2)从当前位置开始往上查找(注:递归返回到上一个节点,也就是查找删除节点过程中经历过的所有节点),判断节点是否平衡。

(3)如果过不平衡,则节点是最小不平衡节点,找到了最小失衡子树,需要进行旋转调整,如果T的左子节点的左子树的高度大于T的左子节点的右子树的高度,进行相应的单旋转。否则进行双旋转。

(4)如果平衡,则只需要更新当前根节点T的高度信息。继续(2步骤),直到查找删除节点过程中经历过的所有节点都遍历完,递归结束。

3、要删除的节点元素值大于当前根节点T值,在右子树中进行删除。

和步骤2相同,不过递归和旋转是在右子树进行。

template <typename T>
bool AvlTree<T>::Delete(AvlNode<T> *&t, T x)
{
    //t为空 未找到要删除的结点
    if (t == NULL)
        return false;
    //找到了要删除的结点
    else if (t->data == x)
    {
        //左右子树都非空
        if (t->left != NULL && t->right != NULL)
        {//在高度更大的那个子树上进行删除操作

            //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
            if (GetHeight(t->left) > GetHeight(t->right))
            {
                t->data = FindMax(t->left)->data;
                Delete(t->left, t->data);
            }
            else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
            {
                t->data = FindMin(t->right)->data;
                Delete(t->right, t->data);
            }
        }
        else
        {//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
            AvlNode<T> *old = t;
            t = t->left ? t->left: t->right;//t赋值为不空的子结点
            delete old;
        }
    }
    else if (x < t->data)//要删除的结点在左子树上
    {//找到删除节点点后的最小不平衡节点
        //递归删除左子树上的结点
        Delete(t->left, x);
        //判断是否仍然满足平衡条件
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            if (GetHeight(t->right->left) > GetHeight(t->right->right))
            {
                //RL双旋转
                t = RL(t);
            }
            else
            {//RR单旋转
                t = RR(t);
            }
        }
        else//满足平衡条件 调整高度信息
        {
            //删除节点后,查询过的节点的高度要发生变化
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }
    else//要删除的结点在右子树上
    {
        //递归删除右子树结点
        Delete(t->right, x);
        //判断平衡情况
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {//找到删除节点点后的最小不平衡节点
            if (GetHeight(t->left->right) > GetHeight(t->left->left))
            {
                //LR双旋转
                t = LR(t);
            }
            else
            {
                //LL单旋转
                t = LL(t);
            }
        }
        else//满足平衡性 调整高度
        {
            //删除节点后,查询过的节点的高度要发生变化
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }

    return true;
}

AVL树的插入时的失衡与调整

平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。

最小失衡子树:新插入或者删除的结点向上查找,以第一个平衡因子的绝对值超过1的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的,如下。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

最小不平衡节点 = 最小失衡子树的根节点。

解决不平衡二叉树的方法是左旋转和右旋转

左旋(右右(后面提到的一种不平衡情况)):

//单旋转
//右右插入导致的不平衡
//t表示最小不平衡节点(最小失衡节点的根节点)
template <typename T>
AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t)
{
    AvlNode<T> *q = t->right;//保存当前节点的右节点
    t->right = q->left;//当前节点的右节点等于当前节点的右节点的左节点
    q->left = t;//当前节点的右节点的左节点等于当前节点(q就变成了根节点)
    t = q;//将最小不平衡节点指向平衡后的q根节点
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

右旋转(左左):

//单旋转
//左左插入导致的不平衡
//t表示最小不平衡节点
template <typename T>
AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t)
{
    AvlNode<T> *q = t->left;//保存当前节点的左节点
    t->left = q->right;//当前节点的左节点等于当前节点的左节点的右节点
    q->right = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

不平衡的四种情况(最小不平衡子树):

https://blog.csdn.net/qq_25940921/article/details/82183093

左左(最小不平衡节点的左节点的左节点插入新节点):

右右(最小不平衡树的右节点的右节点插入新节点):

左右(最小不平衡节点的左节点的右节点插入新节点):

右左(最小不平衡节点的右节点的左节点插入新节点):


 

转载:https://www.cnblogs.com/zhangbaochong/p/5164994.html

 

所有代码:

#include <iostream>
#include <algorithm>
using namespace std;
#pragma once

//平衡二叉树结点
template <typename T>
struct AvlNode
{
    T data;
    int height; //结点所在高度
    AvlNode<T> *left;
    AvlNode<T> *right;
    AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0){}
};

//AvlTree
template <typename T>
class AvlTree
{
public:
    AvlTree<T>(){}
    ~AvlTree<T>(){}
    AvlNode<T> *root;
    //插入结点
    void Insert(AvlNode<T> *&t, T x);
    //删除结点
    bool Delete(AvlNode<T> *&t, T x);
    //查找是否存在给定值的结点
    bool Contains(AvlNode<T> *t, const T x) const;
    //中序遍历
    void InorderTraversal(AvlNode<T> *t);
    //前序遍历
    void PreorderTraversal(AvlNode<T> *t);
    //最小值结点
    AvlNode<T> *FindMin(AvlNode<T> *t) const;
    //最大值结点
    AvlNode<T> *FindMax(AvlNode<T> *t) const;
private:
    //求树的高度
    int GetHeight(AvlNode<T> *t);
    //单旋转 左
    AvlNode<T> *LL(AvlNode<T> *t);
    //单旋转 右
    AvlNode<T> *RR(AvlNode<T> *t);
    //双旋转 右左
    AvlNode<T> *LR(AvlNode<T> *t);
    //双旋转 左右
    AvlNode<T> *RL(AvlNode<T> *t);
};

template <typename T>
AvlNode<T> * AvlTree<T>::FindMax(AvlNode<T> *t) const
{
    if (t == NULL)
        return NULL;
    if (t->right == NULL)
        return t;
    return FindMax(t->right);
}

template <typename T>
AvlNode<T> * AvlTree<T>::FindMin(AvlNode<T> *t) const
{
    if (t == NULL)
        return NULL;
    if (t->left == NULL)
        return t;
    return FindMin(t->left);
}


template <typename T>
int AvlTree<T>::GetHeight(AvlNode<T> *t)
{
    if (t == NULL)
        return -1;
    else
        return t->height;
}


//单旋转
//左左插入导致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t)
{
    AvlNode<T> *q = t->left;//保存当前节点的左节点
    t->left = q->right;//当前节点的左节点等于当前节点的左节点的右节点
    q->right = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

//单旋转
//右右插入导致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t)
{
    AvlNode<T> *q = t->right;//保存当前节点的右节点
    t->right = q->left;//当前节点的右节点等于当前节点的右节点的左节点
    q->left = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

//双旋转
//插入点位于t的左儿子的右子树
template <typename T>
AvlNode<T> * AvlTree<T>::LR(AvlNode<T> *t)
{
    //双旋转可以通过两次单旋转实现
    //对t的左结点进行RR旋转,再对根节点进行LL旋转
    RR(t->left);
    return LL(t);
}

//双旋转
//插入点位于t的右儿子的左子树
template <typename T>
AvlNode<T> * AvlTree<T>::RL(AvlNode<T> *t)
{
    LL(t->right);
    return RR(t);
}

//递归查找插入的位置,t是根节点、父节点、或者是最小不平衡节点,x是插入的值
template <typename T>
void AvlTree<T>::Insert(AvlNode<T> *&t, T x)
{
    if (t == NULL)//当节点为空时插入一个节点
        t = new AvlNode<T>(x);
    else if (x < t->data) //向左遍历
    {
        Insert(t->left, x);//向左递归,直到左节点为空节点,那么就生成一个新的节点作为左节点
        //判断平衡情况,左节点的深度-右节点的深度=平衡因子>1,表示当前节点不平衡,也就是当前节点是 最小不平衡节点
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {
            //分两种情况 左左或左右
            
            if (x < t->left->data)//左左,//插入的节点小于最小不平衡节点的左节点的值,也就是说插入的节点在最小不平衡节点的左节点的左边,右旋转    
                t = LL(t);
            else                  //左右,也就是说插入的节点在最小不平衡节点的左节点的右边
                t = LR(t);
        }
    }
    else if (x > t->data) //向右遍历
    {
        Insert(t->right, x);//向右递归,zhi
        if (GetHeight(t->right) - GetHeight(t->left) > 1)//找到最小不平衡节点
        {
            if (x > t->right->data)//右右,插入的值大于最小不平衡节点的右节点,也就是插入的值在最小不平衡节点的右节点的右边
                t = RR(t);
            else
                t = RL(t);
        }
    }
    else
        ;//数据重复
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;//插入值路径上的所有节点高度都发生变化。计算高度 = 左右高度的最大高度+1;
}

template <typename T>
bool AvlTree<T>::Delete(AvlNode<T> *&t, T x)
{
    //t为空 未找到要删除的结点
    if (t == NULL)
        return false;
    //找到了要删除的结点
    else if (t->data == x)
    {
        //左右子树都非空
        if (t->left != NULL && t->right != NULL)
        {//在高度更大的那个子树上进行删除操作

            //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
            if (GetHeight(t->left) > GetHeight(t->right))
            {
                t->data = FindMax(t->left)->data;
                Delete(t->left, t->data);
            }
            else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
            {
                t->data = FindMin(t->right)->data;
                Delete(t->right, t->data);
            }
        }
        else
        {//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
            AvlNode<T> *old = t;
            t = t->left ? t->left: t->right;//t赋值为不空的子结点
            delete old;
        }
    }
    else if (x < t->data)//要删除的结点在左子树上
    {//找到删除节点点后的最小不平衡节点
        //递归删除左子树上的结点
        Delete(t->left, x);
        //判断是否仍然满足平衡条件
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            if (GetHeight(t->right->left) > GetHeight(t->right->right))
            {
                //RL双旋转
                t = RL(t);
            }
            else
            {//RR单旋转
                t = RR(t);
            }
        }
        else//满足平衡条件 调整高度信息
        {
            //删除节点后,查询过的节点的高度要发生变化
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }
    else//要删除的结点在右子树上
    {
        //递归删除右子树结点
        Delete(t->right, x);
        //判断平衡情况
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {//找到删除节点点后的最小不平衡节点
            if (GetHeight(t->left->right) > GetHeight(t->left->left))
            {
                //LR双旋转
                t = LR(t);
            }
            else
            {
                //LL单旋转
                t = LL(t);
            }
        }
        else//满足平衡性 调整高度
        {
            //删除节点后,查询过的节点的高度要发生变化
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }

    return true;
}

//查找结点
template <typename T>
bool AvlTree<T>::Contains(AvlNode<T> *t, const T x) const
{
    if (t == NULL)
        return false;
    if (x < t->data)
        return Contains(t->left, x);
    else if (x > t->data)
        return Contains(t->right, x);
    else
        return true;
}

//中序遍历
template <typename T>
void AvlTree<T>::InorderTraversal(AvlNode<T> *t)
{
    if (t)
    {
        InorderTraversal(t->left);
        cout << t->data << ' ';
        InorderTraversal(t->right);
    }
}

//前序遍历
template <typename T>
void AvlTree<T>::PreorderTraversal(AvlNode<T> *t)
{
    if (t)
    {
        cout << t->data << ' ';
        PreorderTraversal(t->left);
        PreorderTraversal(t->right);
    }
}
#include "AvlTree.h"

int main()
{
    AvlTree<int> tree;
    int value;
    int tmp;
    cout << "请输入整数建立二叉树(-1结束):" << endl;
    while (cin >> value)
    {
        if (value == -1)
            break;
        tree.Insert(tree.root,value);
    }
    cout << "中序遍历";
    tree.InorderTraversal(tree.root);
    cout << "\n前序遍历:";
    tree.PreorderTraversal(tree.root);
    cout << "\n请输入要查找的结点:";
    cin >> tmp;
    if (tree.Contains(tree.root, tmp))
        cout << "已查找到" << endl;
    else
        cout << "值为" << tmp << "的结点不存在" << endl;
    cout << "请输入要删除的结点:";
    cin >> tmp;
    tree.Delete(tree.root, tmp);
    cout << "删除后的中序遍历:";
    tree.InorderTraversal(tree.root);
    cout << "\n删除后的前序遍历:";
    tree.PreorderTraversal(tree.root);
}

 

 

 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

You can do more

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值