AVL树应为这个条件,从而保证了二叉查找树的高度是受控的,不会出现一般的二叉查找树那样可能随着不断的插入和删除操作,最后退化成一个链表。也是因为这个额外的要求,导致了AVL树的插入和删除操作和一般的二叉查找树不同,每次插入和删除的时候都需要检查左右子树的高度差,做出必要的更新。
下面,先来讨论一下插入操作的情况,插入操作可能发生在节点的左子树或者右子树,那么可以分为以下几种情况:
- 插入前节点的左右子树高度一样,那么无论插入到左子树还是右子树,该节点本身仍然是满足AVL的条件的,但是需要继续往上查看节点的父节点,因为节点本身的高度增加了1
- 插入前节点的左右子树高度相差1,那么当新节点被插入到先前矮一点的那个子树,那么节点的整体高度是不变的,因此,树仍然满足AVL的条件
- 插入前节点的左右子树高度相差1,那么当新节点被插入到先前高一点的那个子树,那么节点的左右子树高度差就会大于1,此时就需要调整了,而调整的情况分为以下四种:
- 插入左孩子的左子树
- 插入左孩子的右子树
- 插入右孩子的左子树
- 插入右孩子的右子树
其中1和4是对称的,可以称之为新节点在外侧插入,2和3也是对称的可以称为新节点在内测插入。外侧插入要想调整平衡比较简单,只需要一次单旋转。而内侧插入则要麻烦一些需要一个双旋转。
我们先分析一下第一种情况,插入节点后新树如下所示:
上图里的K2是插入新节点到X子树中后,第一个出现不平衡的父节点,我们需要旋转它的左孩子K1使整棵树重新平衡,如下图所示。仔细分析一下,插入新节点之前,K1的左右子树的高度必定是一样的,因为如果K1的左子树比右子树高1,那么新节点插入X后,K1将是第一个出现不平衡的父节点而不是K2;而如果K1的左子树比右子树矮,那么插入新节点后,K1的左右子树高度将相同,那么K1还是平衡的而且高度不变,因此K2也肯定仍然是平衡的;因此只剩下一个可能那就是K1之前的左右子树高度是一样的,那么如此我们可以计算出如上图所示的,插入新节点之后各个节点的子树的高度。Z子树的高度必定是和Y子树高度一样,否则在插入任何新节点之前,K2节点就已经不满足平衡条件了。于是,我们选择K1节点,变换成如下图所示的形状后,树的整体高度和插入新节点之前是一样的,因此不需要再继续对原K2的父亲节点进行调整了。这种旋转,称为左旋转。
第四种情况和上面的是对称的,分析基本相似,就不多说了,一会看代码。
再来分析一下第二种情况,插入节点后的新树如下图所示:
上图中,由于新节点是插入到K3的左孩子的右子树中,因此K2整个子树是非空的。如果K2的左右子树不为空,那么新节点可以在左子树或者右子树,这两种情况没有区别,但是在新节点插入K2的子树之前,K2的左右子树的高度必定是相等的,因为如果不等,那么新节点插入到矮一点的子树后,K2的整体高度是不会变的,那么K3和K1也仍然是平衡的;而如果插入到高一点的子树一侧,那么K2就会是第一个不平衡的节点,而不是K3。因此,K2的左右子树必定是等高的,插入新节点后,其中一个的高度增加了1,但是K2子树本身仍然是平衡的,而因为K1在插入新节点后仍然平衡,我们可知K1的左右子树在插入新节点之前必定也是等高的,否则K1和K3要么在插入新节点后仍然保持平衡或者K1成为第一个不平衡的节点。因此我们可以计算出上图中4颗子树的高度。
经过第一次旋转,变成如下所示,仍然不平衡:
需要再经过一次旋转,变成如下所示,才可以恢复平衡:
经过两次旋转后,新树的高度仍然是h+2,因此不需要再继续对K2的父节点进行调整了。
下面是左孩子单旋转,右孩子单旋转,左孩子双旋转,右孩子双旋转的代码片段:
void RotateWithLeftChild(AVLNode*& subroot)
{
if( subroot == NULL || subroot->left == NULL )
throw logic_error("Subroot or its left child is NULL");
AVLNode* leftChild = subroot->left;
subroot->left = leftChild->right;
leftChild->right = subroot;
subroot->height = max ( Height(subroot->left), Height(subroot->right) ) + 1;
leftChild->height = max ( Height(leftChild->left) , Height(subroot) ) + 1;
subroot = leftChild;
}
void RotateWithRightChild(AVLNode*& subroot)
{
if( subroot == NULL || subroot->right == NULL )
throw logic_error("Subroot or its right child is NULL");
AVLNode* rightChild = subroot->right;
subroot->right = rightChild->left;
rightChild->left = subroot;
subroot->height = max ( Height(subroot->left), Height(subroot->right) ) + 1;
rightChild->height = max( Height(subroot),Height(rightChild->right) ) + 1;
subroot = rightChild;
}
void DoubleRotateWithLeftChild(AVLNode*& subroot)
{
RotateWithRightChild(subroot->left);
RotateWithLeftChild(subroot);
}
void DoubleRotateWithRightChild(AVLNode*& subroot)
{
RotateWithLeftChild(subroot->right);
RotateWithRightChild(subroot);
}
下面是基于上面四个旋转操作产生的插入函数代码:
void Insert(const T& v, AVLNode*& subroot)
{
if( subroot == NULL )
{
subroot = new AVLNode(v,NULL,NULL,0);
}
else if( v < subroot->key )
{
Insert(v,subroot->left);
// Height of left child minus height of right child must be one of 0,1,2
// Can't be any other value because of subroot must be a AVL tree before new node inserted
if( Height(subroot->left) - Height(subroot->right) == 2 )
{
// In this case, subroot->left must exist and it is not the newly inserted node
if( v < subroot->left->key )
{
// insert new node to the left subtree of left child
RotateWithLeftChild(subroot);
}
else
{
// insert new node to the right subtree of left child
DoubleRotateWithLeftChild(subroot);
}
}
}
else if( subroot->key < v)
{
Insert(v,subroot->right);
if( Height(subroot->right) - Height(subroot->left) == 2 )
{
if( subroot->right->key < v)
{
RotateWithRightChild(subroot);
}
else
{
DoubleRotateWithRightChild(subroot);
}
}
}
else
{
throw logic_error("Duplicate key");
}
subroot->height = max( Height(subroot->left), Height(subroot->right) ) + 1;
}
删除操作和插入操作类似,因为删除操作会导致子树的高度减小,所以也可能出现不平衡的情况,从而需要作出调整。除此之外,和普通的二叉查找树的删除操作一样,被删除的节点可能含有两个子节点,一个子节点或者无子节点,对于后两种情况,删除节点本身很简单,直接将它的子节点连接到它的父节点就可以了;而对于两个子节点的情况,就要复杂一些,我们可以用二叉查找树的删除操作一样的思路,找出这个节点的中序遍历的前驱节点,然后交换他们的内容,之后从节点的左子树中删除那个前驱节点。
另外,当删除了节点之后,节点的左右子树高度可能变化,下面是可能出现变化的组合(假设删除的节点在根节点的左侧,此处的根节点是指从删除节点往上第一个出现不平衡的父节点,右侧的情况是完全对称的):
- 删除前,左右子树高度一样,删除后,左子树高度减1,树还是平衡的,不需要调整,树的整体高度不变,对更上层的父节点无影响
- 删除前,左子树比右子树高1层,删除后,左右子树高度相同,树还是平衡的,不需要掉正,但是树的整体高度减小了1层,对更上层的父节点有影响
- 删除前,左子树比右子树矮1层,删除后,左子树比右子树高度矮2层,树不平衡了,需要调整,那么此时又可以分为以下三种情况:
a. 右孩子的左右子树本身的高度相同,此时,简单的以右孩子为中心旋转就可以恢复平衡,原树的整体高度不变
b. 右孩子的左子树比右子树高度小1层,此时,简单的以右孩子为中心旋转就可以恢复平衡,原树的整体高度减小1
c. 右孩子的左子树比右子树高度大1层,此时,情况稍微复杂点,需要先以右孩子的左孩子为中心旋转,之后再以右孩子为中心旋转,才可以恢复平衡,原树的整体高度减小1
下面是删除有关的代码:
void RemoveRotateWhenRightHigher(AVLNode*& subroot)
{
// subroot's right child must exist in this case
if(subroot->right == NULL )
throw logic_error("Right child is NULL");
AVLNode* rightChild = subroot->right;
int rightChildHeightDiff = Height(rightChild->left) - Height(rightChild->right);
switch(rightChildHeightDiff)
{
case -1:
RotateWithRightChild(subroot);
break;
case 0:
RotateWithRightChild(subroot);
break;
case 1:
// rightChild's left child shouldn't be NULL in this case
if( rightChild->left == NULL )
throw logic_error("RightChild's left child is NULL");
RotateWithLeftChild(subroot->right);
RotateWithRightChild(subroot);
break;
default:
throw logic_error("RightChild subtree is not an AVL tree");
}
}
void RemoveRotateWhenLeftHigher(AVLNode*& subroot)
{
// subroot's left child must exist in this case
if( subroot->left == NULL )
throw logic_error("Left child is NULL");
AVLNode* leftChild = subroot->left;
int leftChildHeightDiff = Height(leftChild->left) - Height(leftChild->right);
switch(leftChildHeightDiff)
{
case -1:
// leftChild's right child shouldn't be NULL in this case
if( leftChild->right == NULL )
throw logic_error("LeftChild's right child is NULL");
RotateWithRightChild(subroot->left);
RotateWithLeftChild(subroot);
break;
case 0:
RotateWithLeftChild(subroot);
break;
case 1:
RotateWithLeftChild(subroot);
break;
default:
throw logic_error("LeftChild subtree is not an AVL tree");
}
}
void Remove(const T& v, AVLNode* & subroot)
{
if( subroot == NULL )
{
throw logic_error("Can't find target key");
}
else
{
if( subroot->key < v)
{
// delete in right subtree
Remove(v,subroot->right);
if( Height(subroot->left) - Height(subroot->right) == 2)
{
RemoveRotateWhenLeftHigher(subroot);
}
}
else if( v < subroot->key )
{
// delete in left subtree
Remove(v,subroot->left);
if( Height(subroot->right) - Height(subroot->left) == 2)
{
RemoveRotateWhenRightHigher(subroot);
}
}
else
{
// find the target node
if( subroot->left == NULL )
{
// only have right child or no child
AVLNode* temp = subroot;
subroot = subroot->right;
delete temp;
return; // must return now, no need to update new subroot's height, it may not exist
}
else if( subroot->right == NULL )
{
// only have left child
AVLNode* temp = subroot;
subroot = subroot->left;
delete temp;
return; // must return now, no need to update new subroot's height, it may not exist
}
else
{
// have both right and left child
// find the max value in its left subtree
// exchange the value with current node
// then delete the new target in current left subtree
AVLNode* maxLeftChild = FindMax(subroot->left);
T newTarget = maxLeftChild->key;
subroot->key = newTarget;
Remove(newTarget,subroot->left);
if( Height(subroot->right) - Height(subroot->left) == 2)
{
RemoveRotateWhenRightHigher(subroot);
}
}
}
// update current node's height
// so we can update all its parent and make necessary adjustment
subroot->height = max( Height(subroot->left), Height(subroot->right) ) + 1;
}
}
下面是完整的代码:
#ifndef _AVLBINARYSEARCHTREE_
#define _AVLBINARYSEARCHTREE_
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <stack>
using namespace std;
template <typename T>
class AVLBinarySearchTree
{
private:
struct AVLNode
{
T key;
AVLNode* left;
AVLNode* right;
int height;
AVLNode(const T& v,AVLNode* l = NULL,AVLNode* r = NULL, int h = 0) : key(v),left(l),right(r),height(h) {}
};
public:
AVLBinarySearchTree() : root(NULL) {}
~AVLBinarySearchTree() { Clear(); }
AVLBinarySearchTree(const AVLBinarySearchTree& rhs) : root(NULL) { operator=(rhs); }
const AVLBinarySearchTree& operator= (const AVLBinarySearchTree& rhs)
{
if( this != &rhs )
{
this->Clear();
this->Copy(root,rhs.root);
}
return *this;
}
bool IsEmpty() const
{
return root == NULL;
}
void PrintTree() const
{
PrintTree(root);
}
void PreOrderTraverseNonRecursive()
{
if(root != NULL)
{
AVLNode* cur = root;
stack<AVLNode*> rights;
while( cur != NULL || !rights.empty() )
{
if( cur != NULL )
{
PrintNode(cur);
if( cur->right != NULL )
rights.push(cur->right);
cur = cur->left;
}
else
{
cur = rights.top();
rights.pop();
}
}
}
}
void PrintNode(AVLNode* node)
{
if(node != NULL)
{
cout << "[" << node->key << "," << node->height << "], ";
}
else
{
cout << "[], ";
}
}
const T& FindMin() const
{
if(IsEmpty())
throw logic_error("Tree is empty");
AVLNode* targetNode = FindMin(root);
return targetNode->key;
}
const T& FindMax() const
{
if(IsEmpty())
throw logic_error("Tree is empty");
AVLNode* targetNode = FindMax(root);
return targetNode->key;
}
bool Contains( const T& v) const
{
return Contains(root,v);
}
void Clear()
{
Clear(root);
root= NULL;
}
void Insert(const T& v)
{
Insert(v,root);
}
void Remove(const T& v)
{
Remove(v,root);
}
int Height() const
{
if( root == NULL )
return -1;
else
return root->height;
}
private:
void RotateWithLeftChild(AVLNode*& subroot)
{
if( subroot == NULL || subroot->left == NULL )
throw logic_error("Subroot or its left child is NULL");
AVLNode* leftChild = subroot->left;
subroot->left = leftChild->right;
leftChild->right = subroot;
subroot->height = max ( Height(subroot->left), Height(subroot->right) ) + 1;
leftChild->height = max ( Height(leftChild->left) , Height(subroot) ) + 1;
subroot = leftChild;
}
void RotateWithRightChild(AVLNode*& subroot)
{
if( subroot == NULL || subroot->right == NULL )
throw logic_error("Subroot or its right child is NULL");
AVLNode* rightChild = subroot->right;
subroot->right = rightChild->left;
rightChild->left = subroot;
subroot->height = max ( Height(subroot->left), Height(subroot->right) ) + 1;
rightChild->height = max( Height(subroot),Height(rightChild->right) ) + 1;
subroot = rightChild;
}
void DoubleRotateWithLeftChild(AVLNode*& subroot)
{
RotateWithRightChild(subroot->left);
RotateWithLeftChild(subroot);
}
void DoubleRotateWithRightChild(AVLNode*& subroot)
{
RotateWithLeftChild(subroot->right);
RotateWithRightChild(subroot);
}
void RemoveRotateWhenRightHigher(AVLNode*& subroot)
{
// subroot's right child must exist in this case
if(subroot->right == NULL )
throw logic_error("Right child is NULL");
AVLNode* rightChild = subroot->right;
int rightChildHeightDiff = Height(rightChild->left) - Height(rightChild->right);
switch(rightChildHeightDiff)
{
case -1:
RotateWithRightChild(subroot);
break;
case 0:
RotateWithRightChild(subroot);
break;
case 1:
// rightChild's left child shouldn't be NULL in this case
if( rightChild->left == NULL )
throw logic_error("RightChild's left child is NULL");
RotateWithLeftChild(subroot->right);
RotateWithRightChild(subroot);
break;
default:
throw logic_error("RightChild subtree is not an AVL tree");
}
}
void RemoveRotateWhenLeftHigher(AVLNode*& subroot)
{
// subroot's left child must exist in this case
if( subroot->left == NULL )
throw logic_error("Left child is NULL");
AVLNode* leftChild = subroot->left;
int leftChildHeightDiff = Height(leftChild->left) - Height(leftChild->right);
switch(leftChildHeightDiff)
{
case -1:
// leftChild's right child shouldn't be NULL in this case
if( leftChild->right == NULL )
throw logic_error("LeftChild's right child is NULL");
RotateWithRightChild(subroot->left);
RotateWithLeftChild(subroot);
break;
case 0:
RotateWithLeftChild(subroot);
break;
case 1:
RotateWithLeftChild(subroot);
break;
default:
throw logic_error("LeftChild subtree is not an AVL tree");
}
}
void Insert(const T& v, AVLNode*& subroot)
{
if( subroot == NULL )
{
subroot = new AVLNode(v,NULL,NULL,0);
}
else if( v < subroot->key )
{
Insert(v,subroot->left);
// Height of left child minus height of right child must be one of 0,1,2
// Can't be any other value because of subroot must be a AVL tree before new node inserted
if( Height(subroot->left) - Height(subroot->right) == 2 )
{
// In this case, subroot->left must exist and it is not the newly inserted node
if( v < subroot->left->key )
{
// insert new node to the left subtree of left child
RotateWithLeftChild(subroot);
}
else
{
// insert new node to the right subtree of left child
DoubleRotateWithLeftChild(subroot);
}
}
}
else if( subroot->key < v)
{
Insert(v,subroot->right);
if( Height(subroot->right) - Height(subroot->left) == 2 )
{
if( subroot->right->key < v)
{
RotateWithRightChild(subroot);
}
else
{
DoubleRotateWithRightChild(subroot);
}
}
}
else
{
throw logic_error("Duplicate key");
}
subroot->height = max( Height(subroot->left), Height(subroot->right) ) + 1;
}
void Remove(const T& v, AVLNode* & subroot)
{
if( subroot == NULL )
{
throw logic_error("Can't find target key");
}
else
{
if( subroot->key < v)
{
// delete in right subtree
Remove(v,subroot->right);
if( Height(subroot->left) - Height(subroot->right) == 2)
{
RemoveRotateWhenLeftHigher(subroot);
}
}
else if( v < subroot->key )
{
// delete in left subtree
Remove(v,subroot->left);
if( Height(subroot->right) - Height(subroot->left) == 2)
{
RemoveRotateWhenRightHigher(subroot);
}
}
else
{
// find the target node
if( subroot->left == NULL )
{
// only have right child or no child
AVLNode* temp = subroot;
subroot = subroot->right;
delete temp;
return; // must return now, no need to update new subroot's height, it may not exist
}
else if( subroot->right == NULL )
{
// only have left child
AVLNode* temp = subroot;
subroot = subroot->left;
delete temp;
return; // must return now, no need to update new subroot's height, it may not exist
}
else
{
// have both right and left child
// find the max value in its left subtree
// exchange the value with current node
// then delete the new target in current left subtree
AVLNode* maxLeftChild = FindMax(subroot->left);
T newTarget = maxLeftChild->key;
subroot->key = newTarget;
Remove(newTarget,subroot->left);
if( Height(subroot->right) - Height(subroot->left) == 2)
{
RemoveRotateWhenRightHigher(subroot);
}
}
}
// update current node's height
// so we can update all its parent and make necessary adjustment
subroot->height = max( Height(subroot->left), Height(subroot->right) ) + 1;
}
}
int Height(AVLNode* subroot) const
{
return subroot == NULL ? -1 : subroot->height;
}
void Copy(AVLNode* &subroot,AVLNode* source)
{
if(source != NULL)
{
subroot = new AVLNode(source->key,NULL,NULL,source->height);
Copy(subroot->left,source->left);
Copy(subroot->right,source->right);
}
}
void Clear(AVLNode* subroot)
{
if(subroot != NULL )
{
Clear(subroot->left);
Clear(subroot->right);
delete subroot;
}
}
void PrintTree(AVLNode* subroot) const
{
if( subroot != NULL )
{
PrintTree(subroot->left);
cout << subroot->key << ",";
PrintTree(subroot->right);
}
}
AVLNode* FindMin(AVLNode* subroot) const
{
if( subroot == NULL )
return NULL;
while( subroot->left != NULL)
subroot = subroot->left;
return subroot;
}
AVLNode* FindMax(AVLNode* subroot) const
{
if( subroot == NULL )
return NULL;
while( subroot->right != NULL )
subroot = subroot->right;
return subroot;
}
bool Contains(AVLNode* subroot,const T& v) const
{
if( subroot == NULL )
return false;
else if ( v < subroot->key )
return Contains(subroot->left,v);
else if ( subroot->key < v)
return Contains(subroot->right,v);
else
return true;
}
private:
AVLNode* root;
};
#endif