AVL树

刚学avl树,在写程序的过程中遇到一些很小但需要注意的问题。

class AVLTree:public BinarySearchTree<Record>
{
public:
  //下面两个函数调用递归版本插入删除(主要是因为root为private,对于对象而言不可访问)
    Error_code Insert(const Record &newData);
    Error_code Remove( Record &target);//这里不能用const,因为AVLRemove函数会对target做改变
private:
  //下面两个是主要的函数,递归插入删除
    Error_code AVLInsert(BinaryNode<Record>* &subRoot, const Record &newData, bool &taller);
    Error_code AVLRemove(BinaryNode<Record>* &subRoot,  Record &target, bool &shorter);
    //下面两个函数是简单的左右旋转
    void LeftRoated(BinaryNode<Record>* &subRoot);//左旋
    void RightRoated(BinaryNode<Record>* &subRoot);//右旋
    //下面两个函数是插入*过程若不平衡时调用,使树达到平衡
    void LeftBalance(BinaryNode<Record>* &subRoot);//左-右==2
    void RightBalance(BinaryNode<Record>* &subRoot);//右-左==2
    //下面两个函数是删除过程中不平衡时调用,使树达到平衡
    void LeftBalance2(BinaryNode<Record>* &subRoot,bool &shorter);
    void RightBalance2(BinaryNode<Record>* &subRoot,bool &shorter);
};

以下是具体实现,首先旋转:

template<class Record>
void AVLTree<Record>::LeftRoated(BinaryNode<Record>* &subRoot)
{
    if (subRoot == NULL||subRoot->right==NULL)
        cout << "WARNING AT LEFTROATED!" << endl;
    else
    {
        BinaryNode<Record>* rightTree = subRoot->right;
        subRoot->right = rightTree->left;
        rightTree->left = subRoot;
        subRoot = rightTree;
    }
}

template<class Record>
void AVLTree<Record>::RightRoated(BinaryNode<Record>* &subRoot)
{
    if (subRoot == NULL || subRoot->left == NULL)
        cout << "WARNING AT RIGHTROATED!" << endl;
    else
    {
        BinaryNode<Record>* leftTree = subRoot->left;
        subRoot->left = leftTree->right;
        leftTree->right = subRoot;
        subRoot = leftTree;
    }
}

然后再来看插入操作:
函数参数:BinaryNode* &subRoot,要插入的Record &newData,判断插入后树是否增高的taller
分四种情况:
1.subRoot==NULL :插入 ,taller=true
2.subRoot->data==newData :duplicate_error, taller=false
3.subRoot->data>newData:向右边插入,且插入完后需判断taller是否为true。
如果插入后taller=false,即树并未增高,则subRoot的平衡因子不需改变。
如果插入后taller=true,既右边树增高了,这时候就需要判断subRoot的平衡因 子,并且根据subRoot在未插入时的平衡因子来判断树此时是否不平衡(右-左==2)。
subRoot在未插入前的平衡因子有三种情况
*left_higher 插入不会破坏平衡,subRoot平衡因子变为equal _hight,树并未增高,taller=false。
*equal _hight插入不会破坏平衡,subRoot平衡因子变为right _higher,树增高了,taller=true。
*right_higher插入会使树右边高,需调用RightBalance来平衡(注意,在调用这个函数会使树的高度减1)因而taller=false。
(需要注意的是,RightBalance与RightBalance2,尽管大体思想一致,在情况的处理上,后者比前者多,且多出的这种情况恰好使在调用过这个函数后,树的高度不再是减1,具体在下面会说明)
4.subRoot->data< newData:向左边插入。

template<class Record>
Error_code AVLTree<Record>::AVLInsert(BinaryNode<Record>* &subRoot, const Record &newData, bool &taller)
{
    Error_code result = success;
    if (subRoot == NULL)
    {
        subRoot = new AVLNode<Record>(newData);
        taller = true;
    }
    else if (subRoot->data == newData)
        result=duplicate_error;
    else if (subRoot->data > newData)
    {
        result = AVLInsert(subRoot->left, newData, taller);
        if (taller)
            switch (subRoot->getBalance())
            {
            case left_higher:
                LeftBalance(subRoot);
                taller = false;//只要去做旋转,树的高度一定会减少1,所以抵消了插入增加的高度。
                break;
            case equal_hight:
                subRoot->setBalance(left_higher);
                break;
            case right_higher:
                subRoot->setBalance(equal_hight);
                taller = false;
            }
    }
    else
    {
        result = AVLInsert(subRoot->right, newData, taller);
        if(taller)
            switch (subRoot->getBalance())
            {
            case left_higher:
                subRoot->setBalance(equal_hight);
                taller = false;
                break;
            case equal_hight:
                subRoot->setBalance(right_higher);
                break;
            case right_higher:
                RightBalance(subRoot);
                taller = false;
            }
    }
    return result;
}

下面是递归删除函数实现:
函数参数BinaryNode* &subRoot,删除对象Record&target,删除后树是否变矮bool&shorter
有三种情况:
1.subRoot==NULL,这个元素不在not_present,树未变矮,shorter=false。
2.subRoot->data==target ,进行删除操作,此时,又分三种情况:
*subRoot->right==NULL,只需subRoot=subRoot->left,删除target,树自然变矮了,shorter=true
*subRoot->left==NULL,同上
*subRoot->right!=NULL&&subRoot->left!=NULL,如果直接删除,则无法处理左右子树,因而需要找到替代结点,方法向右一步,再一直向左直至某个节点temp->left==NULL。则用temp->data去换subRoot->data,然后去删除temp。
但是如果直接去删,又会产生问题(很难判断删后树是否变矮,即便判断变矮了,然后使subRoot不平衡,此时对subRoot进行平衡),所以这个时候做了一些处理,将仅将subRoot变为temp->data而将target变为temp->data,继续去寻找新的target。当然,如果是在将subRoot先变为temp->data,这个时候递归是无法向下进行的(已经找到了)所以需用subRecord记录下temp->data的值,代递归向下进行后再变。对于这段代码而言,因为subRecord是临时变量,所以生命周期只有这一层。所以需在这层让代码进入另一层的递归,所以这四种情况并不仅仅是 并列的关系,对于3.是不能用else if的,因为若用了else if则被赋予新值的target在这层已经结束,会进入另一层,那么subRecord的记录是无用的了(当然还是可以改的,改后再来补充)
3.subRoot->data>target:向左删除,删除后需判断,树是否变矮,若变矮,则需根据subRoot的平衡因子来决定树是否平衡,以及subRoot新的平衡因子,删之前subRoot平衡因子有三种情况:
*left_higher :树仍然平衡,subRoot新的平衡因子为equal _hight,树变矮了 shorter=true。
*equal_higher:树仍然平衡,subRoot新的平衡因子为right _higher,树未变矮,shorter=false。
*right_higher:树不再平衡,需调用RightBalance2,此时因为RightBalance2比RightBalance多出的情况,调用过这个函数后,树是否减少一层是不一定的,因而需要将shorter这个参数传给RightBalance2这个函数,针对具体情况来修改shorter的值。(这里是需要注意的)
4.subRoot->data< target:向右删除,情况同上。

template<class Record>
Error_code AVLTree<Record>::AVLRemove(BinaryNode<Record>* &subRoot,  Record&target, bool &shorter)
{
    BinaryNode<Record>* temp;
    Record subRecord;
    if (subRoot == NULL)
    {
        shorter = false;
        return not_present;
    }
    if (subRoot->data == target)
    {
        if(subRoot->right==NULL)
        {
            temp = subRoot;
            subRoot =subRoot->left;
            shorter = true;
            delete temp;
            return success;
        }
        else if (subRoot->left==NULL)
        {
            temp = subRoot;
            subRoot = subRoot->right;
            shorter = true;
            delete temp;
            return success;
        }
        else
        {
            temp = subRoot->right;
            while (temp->left)
                temp = temp = temp->left;
            subRecord = temp->data;
            target = temp->data;
            //如果从这里进入循环,而下面用else呢?不行的,因为进入新的循环,subRecord就不再有值
        }
    }
    if (subRoot->data > target)//注意这里不能用else if,因为若target左右有儿子则需要进入这个循环。

    {
        AVLRemove(subRoot->left, target, shorter);
        if (subRecord.TheKey() != 0) subRoot->data = subRecord;
        if(shorter)
            switch (subRoot->getBalance())
            {
            case left_higher:
                subRoot->setBalance(equal_hight);
                break;
            case equal_hight:
                subRoot->setBalance(right_higher);
                shorter = false;
                break;
            case right_higher:
                RightBalance2(subRoot,shorter);
                break;
            }
    }
    else if (subRoot->data < target)//应该是不能用if的,因为如果上个循环删了再进入这个循环,就又遍历一遍,一定找不到或者subRoot为null
    {
        AVLRemove(subRoot->right, target, shorter);
        if (subRecord.TheKey() != 0) subRoot->data = subRecord;
        if(shorter)
            switch (subRoot->getBalance())
            {
            case right_higher:
                subRoot->setBalance(equal_hight);
                break;
            case equal_hight:
                subRoot->setBalance(left_higher);
                shorter = false;
            case left_higher:
                LeftBalance2(subRoot,shorter);
                break;
            }
    }

}

下面我们来对比LeftBalance和LeftBalance2这两个函数。
这里定义了一个变量leftTree =subRoot->left
@先比较两者相同点:
根据leftTree的平衡因子决定是旋转一次还是两次
若leftTree的平衡因子为right_higher又需判断subTree=leftTree->right的平衡因子来决定旋转后三者新的平衡因子。
@两者的不同点:
1.leftTree的平衡因子,LeftBalance中leftTree不存在equal_hight,这种情况,因为在插入时,一定是在左边插入一个结点才造成了左-右>2这种情况,若插入后leftTree为equal _hight则这可树的高度是不会增加的自然也不会不平衡。
而对于删除操作,是对右边的删除使得左边高,所以左边是存在任意哪种情况的。
2.subTree的平衡因子,LeftBalance中同样不存在equal_hight,具体原因明天补充。今天去问了老师发现自己忽略的最简单的情况,即leftTree左右无结点,然后在其右边插入,因而LeftBalance同样存在subTree为equal _hight情况
@需要注意的是:
1.LeftBalance2函数中对于LeftTree多了equal_hight这种情况,而只有在这种情况下树的高度是不变化的,其余情况在调用者两个函数后树的高度均会减一,所以在插入时当调用了LeftBalance这个函数我们可以简单的写taller=false。而在删除过程中调用LeftBalance2这个函数我们并不清楚是否是LeftTree=equal _hight这种情况,因此,需要对树是否变矮具体处理,所以传了shorter的引用。
2.当进行双向旋转时,我们对未处理时subTree情况的判断是为了确定旋转后subRoot,leftTree,subTree,的平衡因子(注意,这三个结点是针对未旋转时树的状态而言的)所以对于subTree的定义需要放在旋转前,否则树的形态已经变化,再去通过leftTree寻找subTree是不正确的(当然进行旋转后subTree一定移到了subRoot的位置,以此来确定亦是可以的)。
3.在进行双向旋转时,先右旋leftTree,再左旋subRoot,因为旋转操作是基于对指针的引用的,因而调用RightRoated时传入的参数是subRoot->left,而非leftTree。

template<class Record>
void AVLTree<Record>::LeftBalance(BinaryNode<Record>* &subRoot)
{
    BinaryNode<Record>* leftTree = subRoot->left;
    switch (leftTree->getBalance())
    {
    case left_higher:
            RightRoated(subRoot);
            subRoot->setBalance(equal_hight);
            leftTree->setBalance(equal_hight);
            break;
    case equal_hight:
        cout << "WARNING AT LEFTBALANCE!" << endl;
            break;
    case right_higher:
        BinaryNode<Record> *subTree = leftTree->right;//注意一定要写在转之前
        LeftRoated(subRoot->left);
        RightRoated(subRoot);
        //BinaryNode<Record> *subTree = leftTree->right;
        switch (subTree->getBalance())
        {
            case left_higher:
                subRoot->setBalance(right_higher);
                leftTree->setBalance(equal_hight);
                break;
            case equal_hight://?会出现这种情况吗?是会的,在leftTree左右无结点右边插入。
                subRoot->setBalance(equal_hight);
                leftTree->setBalance(equal_hight);
                break;
            case right_higher:
                subRoot->setBalance(equal_hight);
                leftTree->setBalance(left_higher);
         }
            subTree->setBalance(equal_hight);
        }
}
template<class Record>
void AVLTree<Record>::LeftBalance2(BinaryNode<Record>* &subRoot,bool &shorter)
{
    BinaryNode<Record>* leftTree = subRoot->left;
    switch (leftTree->getBalance())
    {
    case left_higher:
        RightRoated(subRoot);
        subRoot->setBalance(equal_hight);
        leftTree->setBalance(equal_hight);
        break;
    case equal_hight:
        RightRoated(subRoot);
        subRoot->setBalance(right_higher);
        leftTree->setBalance(left_higher);
        shorter = false;//不同!
        break;
    case right_higher:
        BinaryNode<Record>*subTree = leftTree->right;//
        LeftRoated(subRoot->left);//&
        RightRoated(subRoot);
        //BinaryNode<Record>*subTree = leftTree->right;
        switch (subTree->getBalance())
        {
        case left_higher:
            subRoot->setBalance(right_higher);
            leftTree->setBalance(equal_hight);
            break;
        case equal_hight:
            subRoot->setBalance(equal_hight);
            leftTree->setBalance(equal_hight);
            break;
        case right_higher:
            subRoot->setBalance(equal_hight);
            leftTree->setBalance(left_higher);
        }
        subTree->setBalance(equal_hight);
    }
}

RightBalance和RightBalance2的分析同上

template<class Record>
void AVLTree<Record>::RightBalance(BinaryNode<Record>* &subRoot)
{
    BinaryNode<Record>* rightTree = subRoot->right;
    switch (rightTree->getBalance())
    {
    case right_higher:
        LeftRoated(subRoot);
        subRoot->setBalance(equal_hight);
        rightTree->setBalance(equal_hight);
        break;
    case equal_hight:
        cout << "WARNING AT RIGHTBALANCE!" << endl;
        break;
    case left_higher:
    {
        BinaryNode<Record>* subTree = rightTree->left;//这一句话一定要放在转之前,不然 那个指针都已经指向其他地方了。
        RightRoated(subRoot->right);//注意不能写rightTree,因为是引用,需要用那个指针!!
        LeftRoated(subRoot);
        //BinaryNode<Record>* subTree=rightTree->left;
        switch (subTree->getBalance())
        {
        case right_higher:
            subRoot->setBalance(left_higher);
            rightTree->setBalance(equal_hight);
            break;
        case equal_hight://这种情况是可能的,由空到有一个结点
            subRoot->setBalance(equal_hight);
            rightTree->setBalance(equal_hight);
            break;
        case left_higher:
            subRoot->setBalance(equal_hight);
            rightTree->setBalance(right_higher);
            break;
        }
        subTree->setBalance(equal_hight);
    }
    }
}
template<class Record>
void AVLTree<Record>::RightBalance2(BinaryNode<Record>* &subRoot,bool &shorter)
{
    BinaryNode<Record>* rightTree = subRoot->right;
    switch (rightTree->getBalance())
    {
    case right_higher:
        LeftRoated(subRoot);
        subRoot->setBalance(equal_hight);
        rightTree->setBalance(equal_hight);
        break;
    case equal_hight:
        LeftRoated(subRoot);
        subRoot->setBalance(right_higher);
        rightTree->setBalance(left_higher);
        shorter = false;
        break;
    case left_higher:
        BinaryNode<Record>* subTree = rightTree->left;
        RightRoated(subRoot->right);//&
        LeftRoated(subRoot);
        switch (subTree->getBalance())
        {
        case left_higher:
            subRoot->setBalance(equal_hight);
            rightTree->setBalance(right_higher);
            break;
        case equal_hight:
            subRoot->setBalance(equal_hight);
            rightTree->setBalance(equal_hight);
            break;
        case right_higher:
            subRoot->setBalance(left_higher);
            rightTree->setBalance(equal_hight);
        }
        subRoot->setBalance(equal_hight);

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值