【演算法學習】AVL平衡二叉搜尋樹原理及各項操作程式設計實現(C++)
AVLTree即(Adelson-Velskii-Landis Tree),是加了額外條件的二叉搜尋樹。其平衡條件的建立是為了確保整棵樹的深度為O(nLogn)。平衡條件是任何節點的左右子樹的高度相差不超過1.
在下面的程式碼中,程式設計實現了AVL樹的建立、查詢、插入、刪除、遍歷等操作。採用C++類封裝。
AVL樹中比較複雜的操作時插入和刪除操作。在這裡對插入和刪除操作進行講解。
AVL樹的插入操作
向AVL樹中插入元素可能會導致樹失去平衡。但是,我們只需要調整插入點至根節點的路徑上不滿足平衡狀態的各節點中深度最大的那個即可。假設該最深節點為X。導致X失去平衡可能有四種情況:
(1)插入點位於X的左子結點的左子樹-左左。
(2)插入點位於X的左子結點的右子樹-左右。
(3)插入點位於X的右子結點的左子樹-右左。
(4)插入點位於X的右子結點的左子樹-右右。
情況1和4是對稱的,成為外側插入,可以通過單旋轉解決。而情況2和3也是對稱的,成為內側插入。通過雙旋轉解決。
針對情況1實施單旋轉示例:
針對情況2實施雙旋轉示例:
AVL樹的刪除操作
刪除操作也分為幾種情況:
首先在樹中搜尋是否有節點的元素值等於需要刪除的元素。如未搜尋到,直接返回。否則執行以下操作。
(1)要刪除的節點是當前根節點T。
如果左右子樹都非空。在高度較大的子樹中實施刪除操作。
分兩種情況:
A、左子樹高度大於右子樹高度,將左子樹中最大的那個元素賦給當前根節點,然後刪除左子樹中元素值最大的那個節點。
B、左子樹高度小於右子樹高度,將右子樹中最小的那個元素賦給當前根節點,然後刪除右子樹中元素值最小的那個節點。
如果左右子樹中有一個為空,那麼直接用那個非空子樹或者是NULL替換當前根節點即可。
(2)要刪除的節點元素值小於當前根節點T值,在左子樹中進行刪除。
遞迴呼叫,在左子樹中實施刪除。
這個是需要判斷當前根節點是否仍然滿足平衡條件,
如果滿足平衡條件,只需要更新當前根節點T的高度資訊。
否則,需要進行旋轉調整:
如果T的左子節點的左子樹的高度大於T的左子節點的右子樹的高度,進行相應的單旋轉。否則進行雙旋轉。
(3)要刪除的節點元素值大於當前根節點T值,在右子樹中進行刪除。
過程與上述步驟類似。
具體請看程式碼。
這裡僅列出我程式設計實現的程式碼,如發現bug,還有包涵和指正!
AVLTree.h:
#ifndef AVLTREE_H_INCLUDED
#define AVLTREE_H_INCLUDED
//AVL樹資料結構定義
typedef int ElementType;//AVL數節點包含資料型別
//樹節點
typedef struct AVLNode{
ElementType element;//節點包含的資料元素
AVLNode *left;//節點左子樹
AVLNode *right;//節點右子樹
int height;//節點所在的高度
}*AVLTree;
//AVL tree類封裝
class CAVLTree{
private:
//供內部呼叫的函式
int getHeight(AVLTree);//求得樹的高度
void setHeight(AVLTree,int);//設定節點的高度值
//單旋轉:向右旋轉
AVLTree SingleRightRotate(AVLTree);
//單旋轉:向左旋轉
AVLTree SingleLeftRotate(AVLTree);
//雙旋轉:左右
AVLTree DoubleRightRotate(AVLTree);
//雙旋轉:右左
AVLTree DoubleLeftRotate(AVLTree);
public:
//預設建構函式
CAVLTree();
//解構函式
~CAVLTree();
//建立AVL樹
void createAVLTree(ElementType *data,int n);
//插入節點
AVLTree insertNode(AVLTree T,ElementType val);
//刪除樹中元素值等於某值的節點
AVLTree deleteNode(AVLTree T,const ElementType val);
//搜尋元素值等於某值的節點
AVLTree searchNode(AVLTree,ElementType);
//前序遍歷輸出樹
void preOrder(AVLTree T);
//得到樹中的元素值最大的節點
AVLTree getMaxNode(AVLTree);
//得到樹中的元素值最小的那個節點
AVLTree getMinNode(AVLTree);
AVLTree T;
};
#endif // AVLTREE_H_INCLUDED
AVLTree.cpp:
#include "AVLTree.h"
#include <iostream>
#include <cmath>
#include <cassert>
using namespace std;
CAVLTree::CAVLTree()
{
T = NULL;
}
CAVLTree::~CAVLTree()
{
//if(T)
//{
// if(NULL == T->left && NULL == T->right)
// delete T;
// else{
// delete T->left;
// delete T->right;
// }
//}
deleteTree(T);
}
//依據各元素的資料值,建立AVL樹
void CAVLTree::createAVLTree(ElementType *data,int n)
{
if (T)
{
cout << "The AVL Tree has been created" << endl;
return;
}
if(!n)//元素序列為空
{
T = NULL;
return;
}
for(int i = 0;i < n;++i)
{
T = insertNode(T,*(data + i));
}
return;
}
AVLTree CAVLTree::insertNode(AVLTree T,ElementType val)
{
AVLNode *pNewNode = new AVLNode;
pNewNode->element = val;
pNewNode->left = NULL;
pNewNode->right = NULL;
pNewNode->height = 1;//新節點一定被插入在空節點的位置
if(NULL == T)
{
T = pNewNode;
return T;
}
//需要插入節點的樹非空
//插入的元素已經存在於樹中,不符合要求
if (val == T->element)
{
cout << "元素中有重複,構建AVL樹失敗!" << endl;
return T;
}
//要插入的值小於根節點的值,將其插入左子樹中
if(val < T->element)
{
//將其插入根節點的左子樹中
T->left = insertNode(T->left,val);
//判斷平衡條件是否仍然滿足
if(getHeight(T->left) - getHeight(T->right) > 1)
{
//分兩種情況進行旋轉操作
//插入點位於T的左子結點的左子樹
if(val < T->left->element)
//實施單旋轉-右旋轉
T = SingleRightRotate(T);
else
//插入點位於T的左子結點的右子樹,實施雙右旋轉
T = DoubleRightRotate(T);
}
}
//要插入的值大於根節點的值,將其插入右子樹中
if(val > T->element)
{
T->right = insertNode(T->right,val);
//判斷平衡條件是否仍然滿足
if(getHeight(T->right) - getHeight(T->left) > 1)
{
//節點插入到T的右子節點的右子樹中
if(val > T->right->element)
//實施單旋轉-左旋轉
T = SingleLeftRotate(T);
else
//節點插入到T的右子節點的左子樹上
//實施雙旋轉-左旋轉
T = DoubleLeftRotate(T);
}
}
//更新節點的height值
setHeight(T,max(getHeight(T->left),getHeight(T->right)) + 1);
return T;
}
AVLTree CAVLTree::deleteNode(AVLTree T,const ElementType val)
{
if (!T)
{
cout << "The tree is NULL, delete failed" << endl;
return T;
}
AVLTree searchedNode = searchNode(T,val);
//沒有找到相應的節點,刪除失敗
if (!searchedNode)
{
cout << "Cann't find the node to delete " << val << endl;
return T;
}
//找到了需要刪除的節點
//需要刪除的節點就是當前子樹的根節點
if (val == T->element)
{
//左右子樹都非空
if (T->left && T->right)
{
//在高度更大的那個子樹上進行刪除操作
if (getHeight(T->left) > getHeight(T->right))
{
//左子樹高度大,刪除左子樹中元素值最大的那個節點,同時將其值賦值給根節點
T->element = getMaxNode(T->left)->element;
T->left = deleteNode(T->left,T->element);
}
else{
//刪除右子樹中元素值最小的那個節點,同時將其值賦值給根節點
T->element = getMinNode(T->right)->element;
T->right = deleteNode(T->right,T->element);
}
}
else{
//左右子樹中有一個不為空,那個直接用需要被刪除的節點的子節點替換之即可
AVLTree oldNode = T;
T = (T->left ? T->left : T->right);
delete oldNode;//釋放節點所佔的空間
oldNode = NULL;
}
}
else if (val < T->element)//要刪除的節點在左子樹中
{
//在左子樹中進行遞迴刪除
T->left = deleteNode(T->left,val);
//判斷是否仍然滿足平衡條件
if (getHeight(T->right) - getHeight(T->left) > 1)
{
if (T->right->left > T->right->right)
{
//左雙旋轉
T = DoubleLeftRotate(T);
}
else//進行左單旋轉
T = SingleLeftRotate(T);
}
else
//滿足平衡條件,需要更新高度資訊
T->height = max(getHeight(T->left),getHeight(T->right)) + 1;
}
else//需要刪除的節點在右子樹中
{
T->right = deleteNode(T->right,val);
//判斷是否滿足平衡條件
if (getHeight(T->left) - getHeight(T->right) > 1)
{
if(getHeight(T->left->right) > getHeight(T->left->left))
//右雙旋轉
T = DoubleRightRotate(T);
else
//右單旋轉
T = SingleRightRotate(T);
}
else
//只需調整高度即可
T->height = max(getHeight(T->left),getHeight(T->right)) + 1;
}
return T;
}
AVLTree CAVLTree::searchNode(AVLTree T,ElementType val)
{
if (!T)
{
return NULL;
}
//搜尋到
if (val == T->element)
{
return T;
}
else if (val < T->element)
{
//在左子樹中搜索
return searchNode(T->left,val);
}
else
{
//在右子樹中搜索
return searchNode(T->right,val);
}
}
void CAVLTree::preOrder(AVLTree T)
{
if(!T)
cout << "NULL ";
else
{
cout << T->element << " ";
preOrder(T->left);
preOrder(T->right);
}
}
AVLTree CAVLTree::getMaxNode(AVLTree T)
{
if (!T)//樹為空
{
return NULL;
}
AVLTree tempNode = T;
//向右搜尋直至右子節點為NULL
while(tempNode->right)
{
tempNode = tempNode->right;
}
return tempNode;
}
AVLTree CAVLTree::getMinNode(AVLTree T)
{
if (!T)//樹為空
{
return NULL;
}
AVLTree tempNode = T;
//向左搜尋直至左子結點為NULL
while(tempNode->left)
{
tempNode = tempNode->left;
}
return tempNode;
}
int CAVLTree::getHeight(AVLTree T)
{
return (T == NULL) ? 0 : (T->height);
}
void CAVLTree::setHeight(AVLTree T,int height)
{
T->height = height;
}
//左左外側插入導致的不平衡,採用單旋轉-右旋轉進行修正
//引數解釋:
//T:指向因某種操作失去平衡的最小子樹根節點
AVLTree CAVLTree::SingleRightRotate(AVLTree T)
{
AVLTree xPNode = T;
AVLTree yPNode = T->left;
xPNode->left = yPNode->right;//更改原根節點的左子樹
yPNode->right = xPNode;//更改原根節點左孩子的右子樹
//更新進行了旋轉操作的節點的高度
xPNode->height = max(getHeight(xPNode->left),getHeight(xPNode->right)) + 1;
yPNode->height = max(getHeight(yPNode->left),getHeight(yPNode->right)) + 1;
//原根節點的左孩子節點成為新的根節點
return yPNode;
}
//右右外側插入導致的不平衡,採用單旋轉-左旋轉進行修正
//引數解釋:
//T:指向因某種操作失去平衡的最小子樹根節點
AVLTree CAVLTree::SingleLeftRotate(AVLTree T)
{
AVLTree xPNode = T;
AVLTree yPNode = T->right;
xPNode->right = yPNode->left;//更改原根節點的右孩子
yPNode->left = xPNode;//提升原根節點的右孩子節點為新的根節點
//更新執行了旋轉操作的節點的高度資訊
xPNode->height = max(getHeight(xPNode->left),getHeight(xPNode->right)) + 1;
yPNode->height = max(getHeight(yPNode->left),getHeight(yPNode->right)) + 1;
//返回新的根節點
return yPNode;
}
//插入點位於T的左子結點的右子樹
AVLTree CAVLTree::DoubleRightRotate(AVLTree T)
{
//雙旋轉可以通過兩次單旋轉實現
//第一次單旋轉
assert(T->left != NULL);
//對其左子樹進行一次單旋轉-左旋轉
T->left = SingleLeftRotate(T->left);
//第二次單旋轉
//對新產生的樹進行一次單旋轉-右旋轉
return SingleRightRotate(T);
}
//插入點位於T的右子節點的左子樹
AVLTree CAVLTree::DoubleLeftRotate(AVLTree T)
{
//雙旋轉可以通過兩次單旋轉實現
//第一次單旋轉
assert(T->right != NULL);
//對其右子樹進行一次單旋轉-右旋轉
T->right = SingleRightRotate(T->right);
//第二次單旋轉
//對新產生的樹進行一次單旋轉-左旋轉
return SingleLeftRotate(T);
}
void CAVLTree::deleteTree(AVLTree t)
{
if(NULL == t)
return;
deleteTree(t->left);
deleteTree(t->right);
delete t;
t = NULL;
}
main.cpp:
//平衡二叉樹搜尋樹(AVL tree-Adelson-Velskii-Landis tree)程式設計實現
//作者:江南煙雨
//時間:2012-12-10
#include "AVLTree.h"
#include <iostream>
using namespace std;
int main()
{
const int NumElements = 5;
cout << "AVL樹各項操作程式設計實現:" << endl;
int a[NumElements] ={18,14,20,12,16};
CAVLTree *CAVLTreeObj1 = new CAVLTree();
CAVLTreeObj1->createAVLTree(a,NumElements);
cout << "AVL Tree先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
int insertedVal1 = 15;
CAVLTreeObj1->T = CAVLTreeObj1->insertNode(CAVLTreeObj1->T,insertedVal1);
cout << "向AVL樹中插入元素 " << insertedVal1 << "之後的先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
int insertedVal2 = 16;
CAVLTreeObj1->T = CAVLTreeObj1->insertNode(CAVLTreeObj1->T,insertedVal2);
cout << "向AVL樹中插入元素 " << insertedVal2 << "之後的先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
int minVal = CAVLTreeObj1->getMinNode(CAVLTreeObj1->T)->element;
cout << "樹中最小的元素是:" << minVal << endl;
int maxVal = CAVLTreeObj1->getMaxNode(CAVLTreeObj1->T)->element;
cout << "樹中最大的元素是:" << maxVal << endl;
const int deletedVal1 = 11;
CAVLTreeObj1->T = CAVLTreeObj1->deleteNode(CAVLTreeObj1->T,deletedVal1);
cout << "刪除元素值為 " << deletedVal1 << "的節點之後的樹先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
const int deletedVal2 = 20;
CAVLTreeObj1->T = CAVLTreeObj1->deleteNode(CAVLTreeObj1->T,deletedVal2);
cout << "刪除元素值為 " << deletedVal2 << "的節點之後的樹先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
const int deletedVal3 = 18;
CAVLTreeObj1->T = CAVLTreeObj1->deleteNode(CAVLTreeObj1->T,deletedVal3);
cout << "刪除元素值為 " << deletedVal3 << "的節點之後的樹先序遍歷結果:" << endl;
CAVLTreeObj1->preOrder(CAVLTreeObj1->T);
cout << endl;
const int searchedVal1 = 12;
AVLTree searchedPNode = CAVLTreeObj1->searchNode(CAVLTreeObj1->T,searchedVal1);
if(!searchedPNode)
cout << "cannot find such node whose elemen equals " << searchedVal1 << endl;
else
cout << "search success element " << searchedVal1 << endl;
const int searchedVal2 = 13;
searchedPNode = CAVLTreeObj1->searchNode(CAVLTreeObj1->T,searchedVal2);
if(!searchedPNode)
cout << "cannot find such node whose elemen equals " << searchedVal2 << endl;
else
cout << "search success element " << searchedVal2 << endl;
return 0;
}
執行結果(Win7+VS2008):
關於上述樹操作的畫圖講解如下(手機拍攝,有點不清楚):