AVL樹的介紹
二叉查找樹的深度越小,那麼查找所需要的運算時間越小。一個深度爲log(n)的二叉查找樹,查找算法的時間複雜度也是log(n)。然而,我們在二叉查找樹中已經實現的插入和刪除操作並不能讓保持log(n)的深度。如果我們按照8,7,6,5,4,3,2,1的順序插入節點,那麼就是一個深度爲n的二叉樹,即二叉查找樹退化成單向鏈表。那麼,查找算法的時間複雜度爲n。
AVL樹是高度平衡的查找二叉樹。它的特點是:AVL樹中任何節點的兩個子樹的最大高度差爲1,這樣的平衡特性能保證查找、插入、刪除三大操作的平均或最壞情況下的算法複雜度維持在log(n)。
上面的兩張圖片,左邊的是AVL樹,它的任何節點的兩個子樹的高度差別都<=1;而右邊的不是AVL樹,因爲7的兩顆子樹的高度相差爲2(以2爲根節點的樹的高度是3,而以8爲根節點的樹的高度是1)。
AVL樹的C++實現
1. AVL樹節點
#pragma once
/*
***AVLTreeNode: AVLTree的節點
***key: 關鍵字,節點根據關鍵字排序,用來搜索
***height: 以節點在根的子樹的高度
***leftChild: 節點的左孩子
***rightChild: 節點的右孩子
*/
template <typename T>
struct AVLTreeNode
{
T key;
int height;
AVLTreeNode<T> *leftChild,
*rightChild;
AVLTreeNode(const T &theKey) : key(theKey)
{
height = 0;
leftChild = rightChild = nullptr;
}
AVLTreeNode(const T &theKey, AVLTreeNode<T>* leftChild,
AVLTreeNode<T>* rightChild) : key(theKey)
{
height = 0;
this->leftChild = leftChild;
this->rightChild = rightChild;;
}
};
2. AVL樹接口
template <typename T>
class AVLTree
{
public:
AVLTree();
~AVLTree();
// 獲取AVL樹的高度
int height();
// 前序、中序、後序遍歷"AVL樹"
void preOrder() { preOrder(root); }
void inOrder() { inOrder(root); }
void postOrder() { postOrder(root); }
// 查找"AVL樹"中鍵值爲key的節點
AVLTreeNode<T>* find(const T& key) const;
// 查找最小和最大結點:返回結點的鍵值指針。
T* minimum();
T* maximum();
// 將結點插入到AVL樹中
void insert(const T& theKey);
// 刪除結點
void erase(const T& theKey);
// 銷燬AVL樹
void destroy();
void output() { inOrder(root); cout << endl; }
private:
AVLTreeNode<T> *root; // 根結點
int height(AVLTreeNode<T>* theRoot);
// 前序、中序、後序遍歷"AVL樹"
void preOrder(AVLTreeNode<T>* theRoot) const;
void inOrder(AVLTreeNode<T>* theRoot) const;
void postOrder(AVLTreeNode<T>* theRoot) const;
// 查找最小和最大結點,返回節點指針。
AVLTreeNode<T>* minimum(AVLTreeNode<T>* theRoot) const;
AVLTreeNode<T>* maximum(AVLTreeNode<T>* theRoot) const;
// LL:左左對應的情況(左單旋轉)。
AVLTreeNode<T>* LLRotation(AVLTreeNode<T>* aNode);
// RR:右右對應的情況(右單旋轉)。
AVLTreeNode<T>* RRRotation(AVLTreeNode<T>* aNode);
// LR:左右對應的情況(左雙旋轉)。
AVLTreeNode<T>* LRRotation(AVLTreeNode<T>* aNode);
// RL:右左對應的情況(右雙旋轉)。
AVLTreeNode<T>* RLRotation(AVLTreeNode<T>* aNode);
// 將結點(z)插入到AVL樹中
AVLTreeNode<T>* insert(AVLTreeNode<T>* &theRoot, const T& theKey);
// 刪除AVL樹中的結點(p),並返回被刪除的結點
AVLTreeNode<T>* erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p);
// 銷燬AVL樹
void destroy(AVLTreeNode<T>* &theRoot);
};
AVLTree是AVL樹對應的類。它包含AVL樹的根節點root和AVL樹的基本操作接口。需要說明的是:AVLTree中重載了許多函數。重載的目的是區分內部接口和外部接口,內部接口主要用於外部接口的遞歸實現,而外部接口供用戶使用;例如insert()函數而言,insert(root, theKey)是內部接口,而insert(theKey)是外部接口。
3. 旋轉操作(核心)
如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。這種失去平衡的可以概括爲4種姿態:LL(左左),LR(左右),RR(右右)和RL(右左)。下面給出它們的示意圖:
上圖中的4棵樹都是”失去平衡的AVL樹”,從左往右的情況依次是:LL、LR、RL、RR。除了上面的情況之外,還有其它的失去平衡的AVL樹,如下圖:
上面的兩張圖都是爲了便於理解,而列舉的關於”失去平衡的AVL樹”的例子。總的來說,AVL樹失去平衡時的情況一定是LL、LR、RL、RR這4種之一,它們都由各自的定義:
(1) LL:LeftLeft,也稱爲”左左”。插入或刪除一個節點後,根節點的左子樹的左子樹還有非空子節點,導致”根的左子樹的高度”比”根的右子樹的高度”大2,導致AVL樹失去了平衡。
例如,在上面LL情況中,由於”根節點(8)的左子樹(4)的左子樹(2)還有非空子節點”,而”根節點(8)的右子樹(12)沒有子節點”;導致”根節點(8)的左子樹(4)高度”比”根節點(8)的右子樹(12)”高2。
(2) LR:LeftRight,也稱爲”左右”。插入或刪除一個節點後,根節點的左子樹的右子樹還有非空子節點,導致”根的左子樹的高度”比”根的右子樹的高度”大2,導致AVL樹失去了平衡。
例如,在上面LR情況中,由於”根節點(8)的左子樹(4)的左子樹(6)還有非空子節點”,而”根節點(8)的右子樹(12)沒有子節點”;導致”根節點(8)的左子樹(4)高度”比”根節點(8)的右子樹(12)”高2。
(3) RL:RightLeft,稱爲”右左”。插入或刪除一個節點後,根節點的右子樹的左子樹還有非空子節點,導致”根的右子樹的高度”比”根的左子樹的高度”大2,導致AVL樹失去了平衡。
例如,在上面RL情況中,由於”根節點(8)的右子樹(12)的左子樹(10)還有非空子節點”,而”根節點(8)的左子樹(4)沒有子節點”;導致”根節點(8)的右子樹(12)高度”比”根節點(8)的左子樹(4)”高2。
(4) RR:RightRight,稱爲”右右”。插入或刪除一個節點後,根節點的右子樹的右子樹還有非空子節點,導致”根的右子樹的高度”比”根的左子樹的高度”大2,導致AVL樹失去了平衡。
例如,在上面RR情況中,由於”根節點(8)的右子樹(12)的右子樹(14)還有非空子節點”,而”根節點(8)的左子樹(4)沒有子節點”;導致”根節點(8)的右子樹(12)高度”比”根節點(8)的左子樹(4)”高2。
前面說過,如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。AVL失去平衡之後,可以通過旋轉使其恢復平衡,下面分別介紹”LL(左左),LR(左右),RR(右右)和RL(右左)”這4種情況對應的旋轉方法。
LL的旋轉
LL失去平衡的情況,可以通過一次旋轉讓AVL樹恢復平衡。如下圖:
圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。從中可以發現,旋轉之後的樹又變成了AVL樹,而且該旋轉只需要一次即可完成。
對於LL旋轉,你可以這樣理解爲:LL旋轉是圍繞”失去平衡的AVL根節點”進行的,也就是節點B;而且由於是LL情況,即左左情況,使A繞着B順時針旋轉。將B變成根節點,A變成B的右子樹,”B的右子樹”變成”A的左子樹”。
LLRotation代碼:
// LL型:左左對應的情況(左單旋轉)
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LLRotation(AVLTreeNode<T>* aNode)
{
AVLTreeNode<T>* bNode = aNode->leftChild;
aNode->leftChild = bNode->rightChild;
bNode->rightChild = aNode;
aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;
return bNode;
}
RR的旋轉
理解了LL之後,RR就相當容易理解了。RR是與LL對稱的情況!RR恢復平衡的旋轉方法如下:
RR情況下,A節點繞着B節點逆時針旋轉,使得A成爲B的左孩子,B的左孩子成爲A的右孩子。圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。RR旋轉也只需要一次即可完成。
RRRotation代碼:
// RR型:右右對應的情況(右單旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RRRotation(AVLTreeNode<T>* aNode)
{
AVLTreeNode<T>* bNode = aNode->rightChild;
aNode->rightChild = bNode->leftChild;
bNode->leftChild = aNode;
aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;
return bNode;
}
LR的旋轉
LR失去平衡的情況,需要經過兩次旋轉才能讓AVL樹恢復平衡。如下圖:
第一次旋轉是圍繞”B”進行的”RR旋轉”,第二次是圍繞”C”進行的”LL旋轉”。
LRRotation代碼:
// LR型:左右對應的情況(左雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LRRotation(AVLTreeNode<T>* aNode)
{
aNode->leftChild = RRRotation(aNode->leftChild);
return LLRotation(aNode);
}
RL的旋轉
RL是與LR的對稱情況!RL恢復平衡的旋轉方法如下:
第一次旋轉是圍繞”B”進行的”LL旋轉”,第二次是圍繞”C”進行的”RR旋轉”。
RLRotation代碼:
// RL型:右左對應的情況(右雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RLRotation(AVLTreeNode<T>* aNode)
{
aNode->rightChild = LLRotation(aNode->rightChild);
return RRRotation(aNode);
}
3. 完整的實現代碼
AVL樹的實現文件(AVRTree.h)
#pragma once
#include<iostream>
#include<algorithm>
#include"AVLTreeNode.h"
using namespace std;
template <typename T>
class AVLTree
{
public:
AVLTree() : root(nullptr) { }
~AVLTree() { destroy(); }
// 獲取AVL樹的高度
int height();
// 前序、中序、後序遍歷"AVL樹"
void preOrder() { preOrder(root); }
void inOrder() { inOrder(root); }
void postOrder() { postOrder(root); }
// 查找"AVL樹"中鍵值爲key的節點
AVLTreeNode<T>* find(const T& key) const;
// 查找最小和最大結點:返回結點的鍵值指針。
T* minimum();
T* maximum();
// 將結點插入到AVL樹中
void insert(const T& theKey);
// 刪除結點
void erase(const T& theKey);
// 銷燬AVL樹
void destroy();
void output() { inOrder(root); cout << endl; }
private:
AVLTreeNode<T> *root; // 根結點
int height(AVLTreeNode<T>* theRoot);
// 前序、中序、後序遍歷"AVL樹"
void preOrder(AVLTreeNode<T>* theRoot) const;
void inOrder(AVLTreeNode<T>* theRoot) const;
void postOrder(AVLTreeNode<T>* theRoot) const;
// 查找最小和最大結點,返回節點指針。
AVLTreeNode<T>* minimum(AVLTreeNode<T>* theRoot) const;
AVLTreeNode<T>* maximum(AVLTreeNode<T>* theRoot) const;
// LL:左左對應的情況(左單旋轉)。
AVLTreeNode<T>* LLRotation(AVLTreeNode<T>* aNode);
// RR:右右對應的情況(右單旋轉)。
AVLTreeNode<T>* RRRotation(AVLTreeNode<T>* aNode);
// LR:左右對應的情況(左雙旋轉)。
AVLTreeNode<T>* LRRotation(AVLTreeNode<T>* aNode);
// RL:右左對應的情況(右雙旋轉)。
AVLTreeNode<T>* RLRotation(AVLTreeNode<T>* aNode);
// 將結點(z)插入到AVL樹中
AVLTreeNode<T>* insert(AVLTreeNode<T>* &theRoot, const T& theKey);
// 刪除AVL樹中的結點(p),並返回被刪除的結點
AVLTreeNode<T>* erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p);
// 銷燬AVL樹
void destroy(AVLTreeNode<T>* &theRoot);
};
template <typename T>
int AVLTree<T>::height(AVLTreeNode<T>* theRoot)
{
if(theRoot != nullptr)
return theRoot->height;
return 0;
}
template <typename T>
int AVLTree<T>::height()
{
return height(root);
}
template <typename T>
void AVLTree<T>::preOrder(AVLTreeNode<T>* theRoot) const
{
if (theRoot != nullptr)
{
cout << theRoot->key << " ";
preOrder(theRoot->leftChild);
preOrder(theRoot->rightChild);
}
}
template <typename T>
void AVLTree<T>::inOrder(AVLTreeNode<T>* theRoot) const
{
if (theRoot != nullptr)
{
inOrder(theRoot->leftChild);
cout << theRoot->key << " ";
inOrder(theRoot->rightChild);
}
}
template <typename T>
void AVLTree<T>::postOrder(AVLTreeNode<T>* theRoot) const
{
if (theRoot != nullptr)
{
postOrder(theRoot->leftChild);
postOrder(theRoot->rightChild);
cout << theRoot->key << " ";
}
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::find(const T& theKey) const
{
AVLTreeNode<T>* p = root;
while (p != nullptr)
{
if (theKey < p->key)
p = p->leftChild;
else if (theKey > p->key)
p = p->rightChild;
else
return p;
}
return nullptr;
}
template <typename T>
T* AVLTree<T>::minimum()
{
AVLTreeNode<T>* min = minimum(root);
if (min != nullptr)
return &min->key;
return nullptr;
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::minimum(AVLTreeNode<T>* theRoot) const
{
AVLTreeNode<T>* p = theRoot,
pp = nullptr;
while (p != nullptr)
{
pp = p;
p = p->leftChild;
}
return pp;
}
template <typename T>
T* AVLTree<T>::maximum()
{
AVLTreeNode<T>* max = maximum(root);
if (max != nullptr)
return &max->key;
return nullptr;
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::maximum(AVLTreeNode<T>* theRoot) const
{
AVLTreeNode<T>* p = theRoot,
pp = nullptr;
while (p != nullptr)
{
pp = p;
p = p->rightChild;
}
return pp;
}
// LL型:左左對應的情況(左單旋轉)
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LLRotation(AVLTreeNode<T>* aNode)
{
AVLTreeNode<T>* bNode = aNode->leftChild;
aNode->leftChild = bNode->rightChild;
bNode->rightChild = aNode;
aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;
return bNode;
}
// RR型:右右對應的情況(右單旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RRRotation(AVLTreeNode<T>* aNode)
{
AVLTreeNode<T>* bNode = aNode->rightChild;
aNode->rightChild = bNode->leftChild;
bNode->leftChild = aNode;
aNode->height = max(height(aNode->leftChild), height(aNode->rightChild)) + 1;
bNode->height = max(height(bNode->leftChild), height(bNode->rightChild)) + 1;
return bNode;
}
// LR型:左右對應的情況(左雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::LRRotation(AVLTreeNode<T>* aNode)
{
aNode->leftChild = RRRotation(aNode->leftChild);
return LLRotation(aNode);
}
// RL型:右左對應的情況(右雙旋轉)。
template <typename T>
AVLTreeNode<T>* AVLTree<T>::RLRotation(AVLTreeNode<T>* aNode)
{
aNode->rightChild = LLRotation(aNode->rightChild);
return RRRotation(aNode);
}
template <typename T>
void AVLTree<T>::insert(const T& theKey)
{
insert(root, theKey);
}
//用遞歸將theKey插入到AVL樹中,並維持樹的平衡
template <typename T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &theRoot, const T& theKey)
{
if (theRoot == nullptr)
{
theRoot = new AVLTreeNode<T>(theKey);
if (theRoot == nullptr)
{
cerr << "ERROR: create avltree node failed!" << endl;
return nullptr;
}
}
else if (theKey < theRoot->key)
{//左子樹一側插入節點
theRoot->leftChild = insert(theRoot->leftChild, theKey);
if ( height(theRoot->leftChild) - height(theRoot->rightChild) == 2)
{ //插入節點後,AVL樹失去平衡,平衡因子爲2
if (theKey < theRoot->leftChild->key) //LL型
theRoot = LLRotation(theRoot);
else //LR型
theRoot = LRRotation(theRoot);
}
}
else if (theKey > theRoot->key)
{ //右子樹一側插入節點
theRoot->rightChild = insert(theRoot->rightChild, theKey);
if (height(theRoot->rightChild) - height(theRoot->leftChild) == 2)
{ //插入節點後,AVL樹失去平衡,平衡因子爲-2
if (theKey > theRoot->rightChild->key) //RR型
theRoot = RRRotation(theRoot);
else //RL型
theRoot = RLRotation(theRoot);
}
}
else //theKey == tree->key
cerr << "添加失敗:不允許添加相同的節點!" << endl;
theRoot->height = max(height(theRoot->leftChild), height(theRoot->rightChild));
return theRoot;
}
template <typename T>
void AVLTree<T>::erase(const T& theKey)
{
AVLTreeNode<T>* P = find(theKey); //找到theKey對應的節點
if(p != nullptr)
root = erase(root, p);
}
template <typename T>
AVLTreeNode<T>* AVLTree<T>::erase(AVLTreeNode<T>* &theRoot, AVLTreeNode<T>* p)
{
if (theRoot == nullptr || p == nullptr)
return nullptr;
if (p->key < theRoot->key)
{ //待刪除節點在左子樹一側
theRoot->leftChild = erase(theRoot->leftChild, p);
if (height(theRoot->right) - height(theRoot->leftChild) = 2)
{ //刪除節點後,AVL樹失去平衡,平衡因子爲-2
AVLTreeNode<T>* r = theRoot->rightChild;
if (height(r->leftChild) > height(r->rightChild))
theRoot = RLRotation(theRoot);
else
theRoot = RRRotation(theRoot);
}
}
else if (p->key > theRoot->key)
{ //待刪除節點在右子樹一側
theRoot->rightChild = erase(theRoot->rightChild, p);
if (height(theRoot->leftChild) - height(theRoot->rightChild) == 2)
{ //刪除節點後,AVL樹失去平衡,平衡因子爲2
AVLTreeNode<T>* l = theRoot->leftChild;
if (l->rightChild > l->leftChild)
theRoot = LRRotation(theRoot);
else
theRoot = LLRotation(theRoot);
}
}
else
{ //待刪除的節點就是theRoot
if ((theRoot->leftChild != nullptr) && (theRoot->rightChild != nullptr))
{ //待刪除節點有兩個孩子
if (height(theRoot->leftChild) > height(theRoot->rightChild))
{ //從theRoot的左子樹找到最大節點填補到刪除的節點位置
AVLTreeNode<T>* max = maximum(theRoot->leftChild);
theRoot->key = max->key;
theRoot->leftChild = erase(theRoot->leftChild, max);
}
else
{//從theRoot的左子樹找到最大節點填補到刪除的節點位置
AVLTreeNode<T>* min = minimum(theRoot->rightChild);
theRoot->key = min->key;
theRoot->rightChild = erase(theRoot->rightChild, min);
}
}
else
{ //待刪除的節點最多有一個孩子
AVLTreeNode<T>* tmp = theRoot;
theRoot = (theRoot->leftChild != nullptr) ? theRoot->leftChild : theRoot->rightChild;
delete tmp;
}
}
return theRoot;
}
template <typename T>
void AVLTree<T>::destroy(AVLTreeNode<T>* &theRoot)
{
if (theRoot == nullptr)
return;
destroy(theRoot->leftChild);
destroy(theRoot->rightChild);
delete theRoot;
theRoot = nullptr;
}
template <typename T>
void AVLTree<T>::destroy()
{
destroy(root);
}