标签:
刚学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);
}
}
标签:
原文地址:http://blog.csdn.net/qq_33433822/article/details/51478098