题目
AVL树的主要操作
在本篇博客中我们只实现AVL平衡树的insert、erase、edit和locate以及两个遍历操作,当然还包括一些配套函数,主要论述insert和erase操作,其他诸如successor、minimum等等操作可以参考二叉查找树和红黑树,实现很类似,并没有太大不同。
此外,这些操作我将全部用递归实现,所以和上述两种树以及treap(树堆)节点有所不同,不再引入父指针,因而各种旋转操作也会有所不同,不过原理是一样的。
单旋转和双旋转
假设当前需要平衡调整的子树根为r,由于不平衡,则说明其左右子树高度差为2,。空树高度为-1.有以下四种情形:
1、对r的左孩子的左子树进行了一次插入;
2、对r的左孩子的右子树进行了一次插入;
3、对r的右孩子的右子树进行了一次插入;
4、对r的右孩子的左子树进行了一次插入;
可以看出:情况1和3对称,2和4对称。
左旋和右旋
有了上述对左旋右旋,以及四种情况下的调整策略,不难得出递归的插入程序。
insert_aux(node *r,const Key &k,const Value &v)
插入操作主要是由这个辅助函数递归实现,r是当前子树根。流程如下:
1、若r为空,则说明已经到达叶子,则在此处构造节点,并返回;
2、若r的关键字较大,则向左递归插入节点,若较小则转4;结束后,返回到此处,若需调整则转3,否则继续返回;
3、显然,左子树较高,若是左左插入则调用rightRotate调整;否则为左右插入,则调用leftRightRotate调整。
4、r的关键字较小,须向右递归插入;结束后,返回此处,若需调整则转5,否则继续返回;
5、显然,右子树高,若是右右插入则调用leftRotate调整;否则为右做插入,则调用rightLeftRotate调整。
在这个递归插入过程中有一点需要注意,当递归到叶子成功插入节点后,将逐层返回(其实也就是沿着查找路径逆向返回),在每次返回时都要返回该子树的根,以供上一层更新(左孩子亦或是右孩子),当然,对于最后一次返回将更新树根。返回时,如果该子树经历过调整,那返回的必然是新的子根,那么上一层节点的左/右孩子将会改变;如果没有调整,则返回之前的。这样可以保证整棵树都会得到维护。
erase_aux(node *r,const Key &k)
查找树的删除我们通常都是采用替代法,即如果被删节点有两个孩子,则删除其后继(也就是其右子树的最小关键字节点),若最多只有一个孩子,则删除它本身。在这里我们也采用这种方法,但是也有所不同,我们将用递归,这与通常情况不太一样(参考上面关于三种另外查找树的链接),主要还是因为没有引入父指针的原因,当然,本篇博客开头即说过所有函数皆用递归实现。
流程如下:
1、若r为空,则说明没有该关键字,直接返回;
2、否则,若k小于该节点关键字,则向左递归删除;结束后返回到此处,若需调整则转3,否则直接转4;
3、显然,r的右子树比左子树高2,则相当于右子树插入了一个节点,情况和插入类似,不过也有所不同,即若r右子树有右孩子则只需将r绕着其右孩子做一次左旋(leftRotate)即可恢复平衡;若没有右孩子,则必然存在左孩子,则调用rightLeftRotate即可恢复平衡,返回;
4、若k大于r关键字,则向右递归删除;结束后返回到此处,若需调整则转5,否则直接转6;
5、和3对称,r的左子树比右子树高2,相当于在左子树插入一个节点,那么若r的左孩子存在左孩子,则调用rightRotate即可,若不存在左孩子,则必然存在右孩子,那么调用leftRightRotate即可恢复平衡,返回;
6、到了这一步说明r正是需要删除的节点,若r最多有一个孩子,则转7,否则转8;
7、那么需要删除r本身。先记下r的非空孩子,然后释放r的空间,返回新根,即r的非空孩子;
8、调用过程erase_successor(node*,node*&),删除其后继,把其内容复制到r中,释放期内存,返回根r。
erase_successor(node *r,node *&del)
该函数摘掉r子树的最小值节点存入del,如若需要则调用旋转过程使树平衡。同样地,采用递归实现。流程如下:
1、若r不存在左孩子了,则说明r本身即为该子树最小值节点,那么将r摘下,存入del,返回其右孩子(可能为空);
2、若存在,则递归向左寻找,结束时赶回此处;如若需要则进行调整,和删除算法流程的步骤3一样,返回。
删除过程和插入过程一样,递归结束时将逐层返回,需要注意每次要返回当前子树的新根,以供上层更新,从而维护整棵树。
到此,最繁琐复杂的两个算法插入和删除就介绍完毕了,后面将会给出源代码,注释很详细,结合着看效果更佳。其他几个算法就不介绍了,比较简单,看看源代码即可。
习题
(a)
AVL树的平衡条件比红黑树严格不少,对于高h的AVL树,其节点的最小数目满足上面的公式,可以证明若树节点数目为n,那么树高最多为1.44lg(n+2)-1.328.
(b)
balance(r)
{
if (getHeight(r->left) - getHeight(r->right) == 2)
{//若插入后,左子树过高,失去平衡
if (compare(k, r->left->key))//1、在当前树的左子树的左边插入
r = rightRotate(r, r->left);//则一次右旋即可,并更新新根
else r = leftRightRotate(r, r->left);//2、在当前树的左子树的右边插入,则先左旋,再右旋
}
else if (getHeight(r->right) - getHeight(r->left) == 2)
{//若右子树过高
if (compare(r->right->key, k))//3、在右子树的右边插入
r = leftRotate(r, r->right);
else r = rightLeftRotate(r, r->right);//4、在右子树的左边插入
}
return r;
}
(c) 见代码,注释详细。
//AVL平衡二叉树
#include<iostream>
using namespace std;
#define MAX(i,j) (i > j ? i : j)
template <typename Key,typename Value>
struct node
{//树节点
Key key;
Value value;
int height = 0;
node *left = nullptr;
node *right = nullptr;
node(const Key &k, const Value &v) :key(k), value(v){}
void print()const
{
cout << "key: " << key << "\t\t" << "value: " << value << '\t' << "height: " << height << endl;
}
};
template <typename Key,typename Value,class CompareKey = less<Key>>
class AVLTree
{//AVL树
private:
typedef node<Key, Value> node;
node *root;
CompareKey compare;//比较器
private:
AVLTree(node *r, const CompareKey &c) :root(r), compare(c){}//该构造函数只供内部使用
node* insert_aux(node*, const Key&, const Value&);//插入辅助
node* erase_aux(node*, const Key&);
node* erase_successor(node*,node*&);//当删除节点有两个孩子时,将删除其后继代替
node* locate_aux(node*, const Key&)const;
node* leftRotate(node*, node*);//左旋
node* rightRotate(node*, node*);
node* leftRightRotate(node*, node*);//先左旋,再右旋
node* rightLeftRotate(node*, node*);
void destroy_aux(node*);
int getHeight(node *p)const
{//获得当前节点的高度
if (p == nullptr) return -1;
else return p->height;
}
public:
AVLTree() :root(nullptr), compare(){}
AVLTree(const CompareKey &c) :root(nullptr), compare(c){}
bool empty()const
{
return root == nullptr;
}
void insert(const Key &k, const Value &v)
{
root = insert_aux(root, k, v);
}
void erase(const Key &k)
{
erase_aux(root, k);
}
bool locate(const Key &k)const
{
return (locate_aux(root, k) != nullptr);
}
bool edit(const Key &k, const Value &new_value)
{
node *p = locate_aux(root,k);
if (p != nullptr) p->value = new_value;
return (p != nullptr);
}
void clear()
{//清空树
destroy_aux(root);
root = nullptr;
}
void inTraversal()const;
void preTraversal()const;
~AVLTree(){}
};
template <typename K,typename V,class CK>
node<K, V>* AVLTree<K, V, CK>::insert_aux(node *r,const K &k, const V &v)
{//插入辅助函数
if (r == nullptr)//若已到叶子
r = new node(k, v);
else if (compare(k, r->key))
{//否则,若插入关键字较当前关键字小
r->left = insert_aux(r->left, k, v);//递归向左子树插入
if (getHeight(r->left) - getHeight(r->right) == 2)
{//若插入后,左子树过高,失去平衡
if (compare(k, r->left->key))//1、在当前树的左子树的左边插入
r = rightRotate(r, r->left);//则一次右旋即可,并更新新根
else r = leftRightRotate(r, r->left);//2、在当前树的左子树的右边插入,则先左旋,再右旋
}
}
else if (compare(r->key,k))
{//若插入关键字较大
r->right = insert_aux(r->right, k, v);//则递归右子树插入
if (getHeight(r->right) - getHeight(r->left) == 2)
{//若右子树过高
if (compare(r->right->key, k))//3、在右子树的右边插入
r = leftRotate(r, r->right);
else r = rightLeftRotate(r, r->right);//4、在右子树的左边插入
}
}
r->height = MAX(getHeight(r->left), getHeight(r->right)) + 1;//最后更新该子树根的高度
return r;
}
template <typename K,typename V,class CK>
node<K, V>* AVLTree<K, V, CK>::erase_aux(node *r, const K &k)
{//删除辅助
if (r == nullptr) return nullptr;
else if (compare(k, r->key))
{//若关键字k较小
r->left = erase_aux(r->left, k);//则递归左子树删除
if (getHeight(r->right) - getHeight(r->left) == 2)
{//若删除后,左子树过低
if (r->right->right != nullptr)//a、若当前子树根有右孙子
r = leftRotate(r, r->right);//则左旋
else r = rightLeftRotate(r, r->right);//b、否则,先右旋,再左旋
}
}
else if (compare(r->key, k))
{//若关键字较大,与上述情况对称,不赘述
r->right = erase_aux(r->right, k);
if (getHeight(r->left) - getHeight(r->right) == 2)
{
if (r->left->left != nullptr)
r = rightRotate(r, r->left);
else r = leftRightRotate(r, r->left);
}
}
else
{//若正是要删除的关键字
if (!(r->left == nullptr || r->right == nullptr))
{//若该子树有两个孩子
node *del;
r->right = erase_successor(r->right,del);//则删除后继,del记下将要被删除的节点
r->key = del->key;
r->value = del->value;
delete del;
}
else
{//若至多有一个,则删除其本身
node *child;
if (r->left != nullptr)
child = r->left;
else child = r->right;
delete r;
return child;//并直接返回孩子
}
}
r->height = MAX(getHeight(r->left), getHeight(r->right)) + 1;//更新当前根的高度
return r;
}
template <typename K,typename V,class CK>
node<K, V>* AVLTree<K, V, CK>::erase_successor(node *curr,node *&del)
{//删除某节点后继,即curr子树的最小关键字节点
if (curr->left == nullptr)
{//若当前正是最小值节点
del = curr;//则将删除它
return curr->right;//返回其右孩子
}
else
{//否则
curr->left = erase_successor(curr->left,del);//递归删除最小值节点
if (getHeight(curr->right) - getHeight(curr->left) == 2)
{//若删除后左子树过低
if (curr->right->right != nullptr)//则进行如下调整
curr = leftRotate(curr, curr->right);
else curr = rightLeftRotate(curr, curr->right);
}
return curr;
}
}
template <typename K,typename V,class CK>
node<K, V>* AVLTree<K, V, CK>::locate_aux(node *r,const K &k)const
{
if (r == nullptr)//若没有该关键字
return nullptr;
else if (compare(k, r->key))//小于则往左找
return locate_aux(r->left, k);
else if (compare(r->key, k))//大于则往右找
return locate_aux(r->right, k);
else return r;//等于则直接返回
}
template <typename K,typename V,class CK>
void AVLTree<K, V, CK>::destroy_aux(node *r)
{
if (r == nullptr) return;
if (r->left != nullptr)
destroy_aux(r->left);
if (r->right != nullptr)
destroy_aux(r->right);
delete r;
}
template <typename K,typename V,class CK>
inline node<K, V>* AVLTree<K, V, CK>::leftRotate(node *par, node *child)
{//左旋
par->right = child->left;
child->left = par;
par->height = MAX(getHeight(par->left), getHeight(par->right)) + 1;//更新高度
child->height = MAX(getHeight(child->left), getHeight(child->right)) + 1;
return child;//返回新根
}
template <typename K,typename V,class CK>
inline node<K, V>* AVLTree<K, V, CK>::rightRotate(node *par, node *child)
{//右旋
par->left = child->right;
child->right = par;
par->height = MAX(getHeight(par->left), getHeight(par->right)) + 1;
child->height = MAX(getHeight(child->left), getHeight(child->right)) + 1;
return child;
}
template <typename K,typename V,class CK>
inline node<K, V>* AVLTree<K, V, CK>::leftRightRotate(node *par, node *child)
{//左、右旋
par->left = leftRotate(child, child->right);//先将左孩子绕右孙子左旋,并更新左孩子
return rightRotate(par, par->left);//再将其绕新左孩子右旋
}
template <typename K,typename V,class CK>
inline node<K, V>* AVLTree<K, V, CK>::rightLeftRotate(node *par, node *child)
{//右、左旋
par->right = rightRotate(child, child->left);
return leftRotate(par, par->right);
}
template <typename K,typename V,class CK>
void AVLTree<K, V, CK>::inTraversal()const
{//中序遍历
if (root == nullptr) return;
AVLTree L(root->left,compare);
L.inTraversal();
root->print();
AVLTree R(root->right, compare);
R.inTraversal();
}
template <typename K,typename V,class CK>
void AVLTree<K, V, CK>::preTraversal()const
{//先序遍历
if (root == nullptr) return;
root->print();
AVLTree L(root->left, compare);
L.preTraversal();
AVLTree R(root->right, compare);
R.preTraversal();
}
int main()
{
AVLTree<int, int> t;
for (int i = 1; i <= 20; i += 2)
t.insert(i, i * i);//键、值
t.insert(10, 100);
t.inTraversal();
cout << "------------------------" << endl;
t.preTraversal();
cout << "------------------------" << endl;
t.erase(1);
t.erase(3);
t.erase(5);
t.erase(9);
t.erase(11);
t.erase(13);
t.erase(100);//删除不存在的键
t.inTraversal();
cout << "------------------------" << endl;
t.preTraversal();
getchar();
t.clear();
return 0;
}