码迷,mamicode.com
首页 > 其他好文 > 详细

AVL平衡二叉树

时间:2020-05-05 00:33:31      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:操作   str   更改   遍历   就是   析构函数   拷贝构造函数   code   this   

平衡二叉树

二叉树中所有结点的平衡因子BF的绝对值均小于等于1,即:\(|BF|\leq1\)。平衡因子是,结点的左子树高度减去右子树的高度。平衡因子BF绝对值大于1表示二叉树失衡。

插入失衡

两种情况:

  1. 结点的平衡因子是1,向该结点的左子树插入结点,该结点的平衡因子变为2,导致失衡;
  2. 结点的平衡因子是-1,向该结点的右子树插入结点,该结点的平衡因子变为-2,导致失衡。

如何解决失衡?

关键问题是要找到失衡结点,离插入结点最近的失衡结点。

失衡结点的特点:

  • 一定是插入结点的祖父结点
  • 插入结点时一定经过失衡结点

解决办法:

采用递归的方法进行插入,插入成功后会进行回代,因此在回代的过程中可以判断结点是否失衡,从而能够找到离插入结点最近的失衡结点。

/*
1、判断二叉树是否失衡,若失衡返回true,否则返回false
2、若二叉树失衡,则返回离插入结点最近的结点
*/
bool IsBalanced(BTNode<T> *&T,T key)
{
    if(!T)
    {
        //未找到值为key的结点,进行插入
        Insert(key);
        return true;
    }
    else if(T->data==key)
    {
        return false;//找到结点key,不可插入
    }
    else if(T->data>key)
    {
        bool temp=IsBalanced(T->lchild,key);//如果temp为真,则表示结点插入到T的左子树
        if(temp)
        {
            //判断T的平衡因子,若等于1,则需要调整
        }
    }
    else
    {
        //同上
    }
}

附上完整代码:

#include <iostream>
using namespace std;

//结点结构
template <class T>
class BTNode
{
public:
    //结点数据
    T data;
    //左右孩子指针
    BTNode<T> *lchild, *rchild;
    //添加平衡因子
    int BF;

public:
    //构造函数
    BTNode(T D, int bf = 0, BTNode<T> *l = NULL, BTNode<T> *r = NULL) : data(D), BF(bf), lchild(l), rchild(r) {}
};

//AVL平衡二叉树
template <class T>
class AVLTree
{
    //私有属性
private:
    //二叉树根节点
    BTNode<T> *root;

private:
    //销毁二叉树
    void Destory(BTNode<T> *&rt)
    {
        if(rt)
        {
            this->Destory(rt->lchild);
            this->Destory(rt->rchild);
            delete rt;
        }
    }
    //二叉树查找
    //和二叉排序树的查找方法一样
    bool SearchAVL(BTNode<T> *rt, T key, BTNode<T> *&p, BTNode<T> *f = NULL)
    {
        if (!rt) //查找失败,返回false
        {
            p = f; //p指向查找路径上最后访问的元素
            return false;
        }
        else if (rt->data == key) //查找成功,返回true
        {
            p = rt; //p指向查找到的元素
            return true;
        }
        else if (rt->data > key)
        {
            return this->SearchAVL(rt->lchild, key, p, rt);
        }
        else
        {
            return this->SearchAVL(rt->rchild, key, p, rt);
        }
    }
    //左旋处理
    void L_Rotate(BTNode<T> *&p)
    {
        BTNode<T> *R = p->rchild; //R指向p的左孩子
        p->rchild = R->lchild;    //p的右孩子指向R的左孩子
        R->lchild = p;            //R的左孩子指向p
        p = R;                    //该二叉树的根节点变为R
    }
    //右旋处理
    void R_Rotate(BTNode<T> *&p)
    {
        BTNode<T> *L = p->lchild;
        p->lchild = L->rchild;
        L->rchild = p;
        p = L;
    }
    //左平衡处理,已知结点的平衡因子是+2,因此必定是在该结点的左子树插入的结点
    //二叉排序树的根节点平衡因子的绝对值大于1,需进行平衡处理
    void LeftBalance(BTNode<T> *&p)
    {
        BTNode<T> *L = p->lchild; //L指向p的左孩子
        /*
        判断L的平衡因子
        若平衡因子为1,表示新节点插入在L的左子树
        若平衡因子为-1,表示新节点插入在L的右子树
        */
        switch (L->BF)
        {
        case 1: //结点插入在L的左子树,需进行右旋处理
            this->R_Rotate(p);
            p->BF = 0;
            p->rchild->BF = 0; //右旋处理后,需改变旋转结点的平衡因子
            break;
        case -1:
        { //结点插入在L的右子树,需进行双旋处理
            BTNode<T> *L_R = L->rchild;
            switch (L_R->BF)
            {
            case 1: //结点插入在L_R的左子树
                p->BF = -1;
                L->BF = 0;
                break;
            case 0: //结点L_R就是新插入的结点
                p->BF = 0;
                L->BF = 0;
                break;
            case -1: //结点插入在L_R的右子树
                p->BF = 0;
                L->BF = 1;
                break;
            }
            L_R->BF = 0;
            this->L_Rotate(L); //先左旋处理
            this->R_Rotate(p); //后右旋处理
            break;
        }
        }
    }
    //右平衡处理,已知结点的平衡因子是-2,因此必定是在该结点的右子树插入的结点
    void RightBalance(BTNode<T> *&p)
    {
        BTNode<T> *R = p->rchild;
        switch (R->BF)
        {
        case 1:
        { //新节点插入在R的左子树上,需进行双旋处理
            BTNode<T> *R_L = R->lchild;
            switch (R_L->BF)
            {
            case 1: //结点插入在R_L的左子树上
                p->BF = 0;
                R->BF = -1;
                break;
            case 0: //R_L就是新插入的结点
                p->BF = 0;
                R->BF = 0;
                break;
            case -1: //结点插入在R_L的右子树上
                p->BF = 1;
                R->BF = 0;
                break;
            }
            R_L->BF = 0;
            this->R_Rotate(R); //先右旋处理
            this->L_Rotate(p); //后左旋处理
            break;
        }
        case -1: //新节点插入在R的右子树上,需进行左旋处理
            p->BF = 0;
            R->BF = 0;
            this->L_Rotate(p);
            break;
        }
    }
    //向二叉树中插入结点,并保持平衡
    bool InsertAVL(BTNode<T> *&rt, T key, bool &taller) //taller用来记录二叉树是否长高
    {
        if (!rt) //表示未在二叉树中找到结点值为key的结点,需插入结点
        {
            rt = new BTNode<T>(key, 0, NULL, NULL); //将新节点赋值给rt指针
            taller = true;
            return true;
        }
        else if (rt->data == key) //存在key,返回false
        {
            taller = false;
            return false;
        }
        else if (rt->data > key)
        {
            if (!this->InsertAVL(rt->lchild, key, taller)) //未插入结点
            {
                return false;
            }
            if (taller) //二叉树长高,表示递归回到rt结点
            {
                switch (rt->BF)
                {
                case 1: //结点rt的平衡因子为1,而又在左子树新插入结点,故结点rt失衡,需调整
                    this->LeftBalance(rt);
                    taller = false; //维护状态,在失衡结点调整后,其祖父结点的平衡因子不会变,故不用更改,结束此模块递归
                    break;
                case 0: //结点rt的平衡因子为0,在左子树插入结点,结点rt不会失衡,不用调整
                    rt->BF = 1;
                    taller = true; //插入新节点,二叉树高度增加,继续此模块递归,找到失衡结点
                    break;
                case -1: //结点rt的平衡因子为-1,在左子树插入结点,结点rt不会失衡,不用调整
                    rt->BF = 0;
                    taller = false; //插入新节点,二叉树高度未增,结束此模块递归
                    break;
                }
            }
            return true;
        }
        else
        {
            if (!this->InsertAVL(rt->rchild, key, taller)) //未插入结点
            {
                return false;
            }
            if (taller) //二叉树长高,表示递归回到rt结点
            {
                switch (rt->BF)
                {
                case 1: //结点rt的平衡因子为1,而又在右子树新插入结点,故结点rt不会失衡
                    rt->BF = 0;
                    taller = false; //插入新节点,二叉树高度未增,结束此模块递归
                    break;
                case 0: //结点rt的平衡因子为0,在右子树插入结点,结点rt不会失衡,不用调整
                    rt->BF = -1;
                    taller = true; //插入新节点,二叉树高度增加,继续此模块递归,找到失衡结点
                    break;
                case -1: //结点rt的平衡因子为-1,在右子树插入结点,结点rt会失衡,用调整
                    this->RightBalance(rt);
                    taller = false; //维护状态,在失衡结点调整后,其祖父结点的平衡因子不会变,故不用更改,结束此模块递归
                    break;
                }
            }
            return true;
        }
    }
    //前序遍历
    void PreOrder(BTNode<T> *rt)
    {
        if (rt)
        {
            cout << rt->data << " ";
            this->PreOrder(rt->lchild);
            this->PreOrder(rt->rchild);
        }
    }
    //中序遍历
    void InOrder(BTNode<T> *rt)
    {
        if(rt)
        {
            this->InOrder(rt->lchild);
            cout<<rt->data<<" ";
            this->InOrder(rt->rchild);
        }
    }

public:
    //构造函数
    AVLTree() : root(NULL) {}
    //拷贝构造函数
    AVLTree(const AVLTree<T> &t) {}
    //销毁二叉树
    void Destory() 
    {
        this->Destory(this->root);
        this->root=NULL;
    }
    //析构函数
    ~AVLTree()
    {
        this->Destory(this->root);
    }
    //前序遍历
    void PreOrder()
    {
        this->PreOrder(this->root);
    }
    //中序遍历
    void InOrder()
    {
        this->InOrder(this->root);
    }
    //二叉树查找
    bool Search(T key, BTNode<T> *&p = NULL)
    {
        return this->SearchAVL(this->root, key, p, NULL);
    }
    //插入操作
    bool Insert(T key)
    {
        bool p;
        return this->InsertAVL(this->root, key, p);
    }
};

int main()
{
    AVLTree<int> temp;
    for (int i = 0; i < 10; i++)
    {
        temp.Insert(i);
    }
    temp.PreOrder();
    cout<<endl;
    temp.InOrder();
    cout<<endl;

    system("pause");
    return 0;
}

AVL平衡二叉树

标签:操作   str   更改   遍历   就是   析构函数   拷贝构造函数   code   this   

原文地址:https://www.cnblogs.com/cqy-wt1124/p/12821277.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!