目錄
1.AVL樹(shù)引入
(1)二叉搜索樹(shù)缺點(diǎn)
(2)AVL樹(shù)簡(jiǎn)介
? [1]問(wèn)題的解決
? [2]AVL樹(shù)的性質(zhì)
2.AVL樹(shù)的插入旋轉(zhuǎn)操作
(1)術(shù)語(yǔ)解釋
(2)左單旋
? [1]插入到右側(cè)的左邊
? [2]插入到右側(cè)的右邊
(3)右單旋
? [1]插入到左側(cè)的左邊
? [2]插入到左側(cè)的右邊
(4)左右雙旋
? [1]插入到右側(cè)的左邊
? [2]插入到右側(cè)的右邊
(5)右左雙旋
? [1]插入到左側(cè)的左邊
? [2]插入到左側(cè)的右邊
3.AVL樹(shù)的局部實(shí)現(xiàn)
(1)AVL樹(shù)的結(jié)點(diǎn)類
(2)AVL樹(shù)成員變量
(3)無(wú)參構(gòu)造?
(4)左單旋
(5)右單旋
(6)左右雙旋
(7)右左雙旋
(8)插入函數(shù)
(9)中序遍歷
(10)析構(gòu)函數(shù)
(11)驗(yàn)證函數(shù)
4.AVL樹(shù)的整體實(shí)現(xiàn)與驗(yàn)證
(1)AVL樹(shù)的整體實(shí)現(xiàn)
(2)AVL樹(shù)的驗(yàn)證
AVL樹(shù)也叫平衡二叉搜索樹(shù),它是二叉搜索樹(shù)的升級(jí)版,修復(fù)了二叉樹(shù)中存在的缺點(diǎn),但因此也帶來(lái)了更復(fù)雜的邏輯,所以本文需要搭配大量圖片來(lái)理解。
? 本文在win10系統(tǒng)的vs2019中驗(yàn)證。
1.AVL樹(shù)引入 ? (1)二叉搜索樹(shù)缺點(diǎn)? 來(lái)看這幅圖,這是用二叉搜索樹(shù)存儲(chǔ)的五個(gè)元素:由于二叉搜索樹(shù)的特性,導(dǎo)致了這棵樹(shù)成為了單支樹(shù)。這樣二叉搜索樹(shù)在中序遍歷時(shí)效率就會(huì)降低,為了解決這樣的缺點(diǎn),又引入了AVL樹(shù)。(平衡二叉搜索樹(shù))
? AVL樹(shù)是為了解決上述問(wèn)題而出現(xiàn)。如何解決呢?
? 當(dāng)向二叉搜索樹(shù)中插入新結(jié)點(diǎn)后,如果能保證每個(gè)結(jié)點(diǎn)的左右子樹(shù)高度之差的絕對(duì)值不超過(guò)1,就可以降低樹(shù)的高度,從而減少平均搜索長(zhǎng)度。
? 如何使高度之差的絕對(duì)值不超過(guò)1就是AVL樹(shù)的難點(diǎn),需要對(duì)AVL樹(shù)進(jìn)行調(diào)整,才可以達(dá)到這樣的狀態(tài),這也是本文的重點(diǎn)。
? [2]AVL樹(shù)的性質(zhì)? 一棵AVL樹(shù)只有兩個(gè)狀態(tài):
? 較高左子樹(shù):如圖,以30為根結(jié)點(diǎn)的樹(shù),左子樹(shù)有2層,右子樹(shù)只有1層,因此左子樹(shù)就是較高左子樹(shù)。
? 較高右子樹(shù):如圖,以70為根結(jié)點(diǎn)的樹(shù),左子樹(shù)只有1層,右子樹(shù)有2層,因此右子樹(shù)就是較高右子樹(shù)。
? 較高左子樹(shù)的左側(cè)/右側(cè):以30為根結(jié)點(diǎn)的樹(shù),將新結(jié)點(diǎn)作為0的孩子插入。也就是將新結(jié)點(diǎn)插入較高左子樹(shù)的左側(cè)。
? 較高右子樹(shù)的左側(cè)/右側(cè):以70為根結(jié)點(diǎn)的樹(shù),將新結(jié)點(diǎn)作為90的孩子插入。也就是將新結(jié)點(diǎn)插入到較高右子樹(shù)的右側(cè)。
?平衡因子:本文中指定用某個(gè)結(jié)點(diǎn)的右子樹(shù)的高度減去左子樹(shù)的高度的值作為平衡因子。如:30這個(gè)結(jié)點(diǎn)的平衡因子就是(1 - 2) == -1。90這個(gè)結(jié)點(diǎn)的平衡因子就是0。
? 接下來(lái)講解的旋轉(zhuǎn)操作就需要用到平衡因子,本文用bf表示平衡因子。
(2)左單旋? 將新結(jié)點(diǎn)插入到較高右子樹(shù)的右側(cè),分為兩類:插入到右側(cè)的左邊,插入到右側(cè)的右邊。(以下幾種情況同理,都會(huì)分左右兩邊) 此時(shí)需要左單旋。
? [1]插入到右側(cè)的左邊? 如圖:左右兩棵子樹(shù)的根結(jié)點(diǎn)用parent表示(注意:這里的根結(jié)點(diǎn)不只是整棵樹(shù)的根結(jié)點(diǎn),也可以是一棵子樹(shù)的根結(jié)點(diǎn)),右子樹(shù)的根結(jié)點(diǎn)用subR表示,右子樹(shù)的左孩子的根節(jié)點(diǎn)用subRL表示(很好記的,右子樹(shù)的左孩子就是RrightLeft---->RL)。
? 此時(shí)圖一中:parent結(jié)點(diǎn)的平衡因子bf就是 (2 - 1) == 1,subR的平衡因子是0,subRL的平衡因子是0。(后面就不會(huì)仔細(xì)介紹圖片的細(xì)節(jié)了,因?yàn)槎己瓦@個(gè)一樣) 圖一此時(shí)的平衡因子的絕對(duì)值都不大于1,說(shuō)明符合AVL樹(shù)的要求。
? 圖二:將75插入到了右側(cè)的左邊。AVL樹(shù)的插入算法和二叉平衡樹(shù)相比,就是多了各種旋轉(zhuǎn)操作,因此如何尋找結(jié)點(diǎn)的合適位置和二叉搜索樹(shù)是一樣的:根結(jié)點(diǎn)左側(cè)比根節(jié)點(diǎn)小,根節(jié)點(diǎn)右側(cè)比根結(jié)點(diǎn)大。
? 圖二中插入了新結(jié)點(diǎn)后,就應(yīng)該把樹(shù)中的各個(gè)結(jié)點(diǎn)的平衡因子更新,更新后subR的平衡因子更新為1,parent的平衡因子更新為2。(平衡因子的更新是從下到上進(jìn)行更新) 更新后parent的平衡因子的絕對(duì)值大于1,不滿足AVL樹(shù),就應(yīng)該進(jìn)行調(diào)整。
? 調(diào)整方法:首先將parent結(jié)點(diǎn)的右孩子指針指向subRL,然后將subR的左孩子指針指向parent。這個(gè)過(guò)程簡(jiǎn)單的就是說(shuō),兒變爹,爹變兒。
? 調(diào)整后:不要忘記從修改這幾個(gè)結(jié)點(diǎn)的平衡因子,可以發(fā)現(xiàn),這個(gè)情況是將三個(gè)結(jié)點(diǎn)的平衡因子都變成0。
? 這種調(diào)整就像是把雙親結(jié)點(diǎn)旋轉(zhuǎn)到了孩子結(jié)點(diǎn)的左側(cè),所以叫做左單旋。
? [2]插入到右側(cè)的右邊? 新結(jié)點(diǎn)插入到較高右子樹(shù)的右側(cè)的右邊。這個(gè)情況的處理方法和插入到左邊的處理方式完全相同,這里就不贅述了。
(3)右單旋? 將新結(jié)點(diǎn)插入到較高左子樹(shù)的左側(cè)。
? [1]插入到左側(cè)的左邊將新結(jié)點(diǎn)插入到較高左子樹(shù)的左側(cè)的左邊,依舊需要標(biāo)結(jié)點(diǎn),作為根節(jié)點(diǎn)的parent結(jié)點(diǎn),根結(jié)點(diǎn)的左孩子subL結(jié)點(diǎn),左孩子的右孩子subLR,同時(shí)標(biāo)出它們的平衡因子。
? 這種情況的解決的辦法:首先讓parent結(jié)點(diǎn)的左孩子指針指向subLR結(jié)點(diǎn),然后讓subL結(jié)點(diǎn)的右孩子指針指向parent結(jié)點(diǎn),然后更新平衡因子,這種情況依舊是將這幾個(gè)結(jié)點(diǎn)平衡因子更新為0。
? 這就像是把雙親結(jié)點(diǎn)旋轉(zhuǎn)到了孩子結(jié)點(diǎn)的右側(cè),所以叫做右單旋。
? [2]插入到左側(cè)的右邊將新結(jié)點(diǎn)插入到較高左子樹(shù)的左側(cè)的右邊,這種情況的處理方法和插入到左邊的方法相同,這里也不再贅述了。
(4)左右雙旋? 雙旋比單旋要復(fù)雜,所以要仔細(xì)看圖。
? 將新結(jié)點(diǎn)插入到較高左子樹(shù)的右側(cè)。
? [1]插入到右側(cè)的左邊如圖二:將新結(jié)點(diǎn)插入到了較高左子樹(shù)的右側(cè)的左邊,更新平衡因子后需要調(diào)整。此時(shí)如果按照右單旋的方式調(diào)整,調(diào)整完后依然不滿足條件。(讀者可以試著按右單旋畫(huà)圖,這樣就很好理解了)
? 解決辦法:此時(shí)需要把以subL為根的子樹(shù)進(jìn)行左單旋,從而將整棵樹(shù)轉(zhuǎn)換成可以使用右單旋的狀態(tài),然后把以parent為根的樹(shù)進(jìn)行右單旋。
? 具體操作:首先把以subL為根的子樹(shù)進(jìn)行左單旋:先讓subL的右孩子指針指向subLR的左孩子,然后讓subLR的左孩子指針指向subL結(jié)點(diǎn),最后讓parent的左孩子指針指向subLR結(jié)點(diǎn)。左單旋完成。
然后把以parent為根的樹(shù)進(jìn)行右單旋:這里就和右單旋的示意圖一樣了,但為了避免命名混亂,這里希望讀者可以對(duì)照著右單旋的圖看一下。
? 注意:這里的三個(gè)結(jié)點(diǎn)的命名始終沒(méi)有變過(guò),這三個(gè)名字一直都對(duì)應(yīng)這三個(gè)結(jié)點(diǎn),這很重要,因?yàn)檫@里跟前面的單旋相比多了一個(gè)步驟。
? 大家仔細(xì)看以下前面的單旋,到最后,單旋的三個(gè)結(jié)點(diǎn)的平衡因子全都更新成0。但是雙旋不一樣,仔細(xì)看圖6,parent的平衡因子是1。
? 總結(jié):左右雙旋中,如果圖二的subLR的平衡因子是-1,那么圖六中parent的平衡因子將被更新為1。如果圖二的subLR的平衡因子是1,那么圖六中subL的平衡因子將被更新為-1。(第二個(gè)是插入到右側(cè)的右邊的特點(diǎn),這里提前給出)
? [2]插入到右側(cè)的右邊如圖二:將新結(jié)點(diǎn)插入到了較高左子樹(shù)的右側(cè)的右邊,需要進(jìn)行雙旋來(lái)調(diào)整。解決辦法和上面差不多,但這里還是要寫(xiě)一下,因?yàn)殡p旋比較復(fù)雜。
? 解決辦法:首先把以subL為根的子樹(shù)進(jìn)行左單旋:先把subL的右孩子指針指向subLR的左孩子,然后讓subLR的左孩子指針指向subL結(jié)點(diǎn),最后讓parent的左孩子指針指向subLR結(jié)點(diǎn)。
然后把以parent為根的樹(shù)進(jìn)行右單旋,同時(shí)雙旋全都需要考慮平衡因子和單旋的不同。
總結(jié):左右雙旋中,如果圖二的subLR的平衡因子是-1,那么圖六中parent的平衡因子將被更新為1。如果圖二的subLR的平衡因子是1,那么圖六中subL的平衡因子將被更新為-1。
(5)右左雙旋? 將新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)。
? [1]插入到左側(cè)的左邊把新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)的左邊,這種情況也是無(wú)法通過(guò)單旋解決的,需要使用雙旋。
? 如圖二:把新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)的左邊,同時(shí)更新平衡因子,發(fā)現(xiàn)不滿足AVL樹(shù)的條件。
? 解決辦法:把以subR為根的樹(shù)進(jìn)行右單旋,然后把以parent為根的樹(shù)進(jìn)行左單旋,然后和左右雙旋一樣,需要更新圖六的平衡因子。
總結(jié):左右雙旋中,如果圖二的subRL的平衡因子是-1,那么圖六中subR的平衡因子將被更新為1。如果圖二的subRL的平衡因子是1,那么圖六中parent的平衡因子將被更新為-1。
? [2]插入到左側(cè)的右邊把新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)的右邊,這種情況也是無(wú)法通過(guò)單旋解決的,需要使用雙旋。
? 如圖二:把新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)的右邊,同時(shí)更新平衡因子,發(fā)現(xiàn)不滿足AVL樹(shù)的條件。
? 解決辦法:把以subR為根的樹(shù)進(jìn)行右單旋,然后把以parent為根的樹(shù)進(jìn)行左單旋,然后和左右雙旋一樣,需要更新圖六的平衡因子。
總結(jié):左右雙旋中,如果圖二的subRL的平衡因子是-1,那么圖六中subR的平衡因子將被更新為1。如果圖二的subRL的平衡因子是1,那么圖六中parent的平衡因子將被更新為-1。
3.AVL樹(shù)的局部實(shí)現(xiàn)? AVL樹(shù)的刪除操作也比較繁瑣,因此本文只主要介紹插入操作。同時(shí)為了方便操作,我們規(guī)定這里實(shí)現(xiàn)的AVL樹(shù)中的元素唯一,不存在相同的。
(1)AVL樹(shù)的結(jié)點(diǎn)類? AVL樹(shù)就是升級(jí)版的二叉搜索樹(shù),因此和二叉搜索樹(shù)一樣也需要實(shí)現(xiàn)結(jié)點(diǎn)類來(lái)存儲(chǔ)元素。因?yàn)榻Y(jié)點(diǎn)中可能存儲(chǔ)各種類型的元素,因此也是模板。
? 結(jié)點(diǎn)中有三個(gè)指針:指向此結(jié)點(diǎn)左右孩子的指針,指向此結(jié)點(diǎn)雙親的指針。保存元素的變量,保存平衡因子的變量。
//結(jié)點(diǎn)類
templatestruct AVLTreeNode {
AVLTreeNode* left;//左孩子指針
AVLTreeNode* right;//右孩子指針
AVLTreeNode* parent;//雙親指針
T value;//存儲(chǔ)元素的變量
int bf;//平衡因子
//用傳入的值構(gòu)造結(jié)點(diǎn)
AVLTreeNode(const T& _value = T())
:left(nullptr)
,right(nullptr)
,parent(nullptr)
,value(_value)
,bf(0)
{}
};
(2)AVL樹(shù)成員變量? AVL樹(shù)的成員變量只有指向根結(jié)點(diǎn)的指針。這里需要用typedef關(guān)鍵字給結(jié)點(diǎn)類起別名,使用會(huì)更方便。
//AVL樹(shù)類
templateclass AVLTree {
//給結(jié)點(diǎn)類起別名,方便使用
typedef AVLTreeNodeNode;
private:
//指向根結(jié)點(diǎn)的結(jié)點(diǎn)指針
Node* root;
};
(3)無(wú)參構(gòu)造?//無(wú)參構(gòu)造
//讓根結(jié)點(diǎn)指針指向空即可
AVLTree()
:root(nullptr)
{}
(4)左單旋? 新結(jié)點(diǎn)插入到較高右子樹(shù)的右側(cè)。
? 根據(jù)圖片講解中的步驟進(jìn)行理解。
? 首先讓parent的右孩子指針指向subL,然后讓subR的左孩子指針指向parent,同時(shí)記得更新結(jié)點(diǎn)的雙親指針,最后更新平衡因子。
//左單旋
//以下過(guò)程建議搭配圖片理解
void RotateLeft(Node* parent) {
//這里的指針設(shè)計(jì)在講解旋轉(zhuǎn)的時(shí)候已經(jīng)解釋過(guò)
Node* subR = parent->right;
Node* subRL = subR->left;
//用pparent(祖父結(jié)點(diǎn))來(lái)保存parent結(jié)點(diǎn)的雙親
//因?yàn)閜arent并不一定是整棵樹(shù)的根,還有可能只是一棵子樹(shù)的根
Node* pparent = parent->parent;
//第一步:首先讓雙親的右孩子指針指向subRL
parent->right = subRL;
//之所以要判斷是因?yàn)閟ubRL結(jié)點(diǎn)可能不存在
if (subRL)
//更新雙親
subRL->parent = parent;
//第二步:讓subR的左孩子指針指向parent
subR->left = parent;
//不要忘記給這些結(jié)點(diǎn)更新它們的雙親
parent->parent = subR;
subR->parent = pparent;
//祖父結(jié)點(diǎn)是空,說(shuō)明parent結(jié)點(diǎn)是整棵樹(shù)的根結(jié)點(diǎn)
if (pparent == nullptr) {
root = subR;
}
//說(shuō)明parent是子樹(shù)的根結(jié)點(diǎn)
else {
//原本祖父結(jié)點(diǎn)的孩子是parent
//經(jīng)過(guò)旋轉(zhuǎn)他的孩子變成了subR
//需要判斷parent是祖父結(jié)點(diǎn)的左孩子還是右孩子,然后讓對(duì)應(yīng)指針指向subR結(jié)點(diǎn)
if (pparent->left == parent) {
pparent->left = subR;
}
else {
pparent->right = subR;
}
}
//更新平衡因子
parent->bf = subR->bf = 0;
}
(5)右單旋? 新結(jié)點(diǎn)插入到較高左子樹(shù)的左側(cè)。
? 與左單旋相似,也是兩步走,然后更新雙親指針和平衡因子。
//右單旋
void RotateRight(Node* parent) {
Node* subL = parent->left;
Node* subLR = subL->right;
//祖父結(jié)點(diǎn)
Node* pparent = parent->parent;
//第一步:讓parent的左孩子指針指向subLR
parent->left = subLR;
//之所以要判斷是因?yàn)閟ubLR結(jié)點(diǎn)可能不存在
if (subLR)
subLR->parent = parent;
//第二步:讓subL的右孩子指針指向parent
subL->right = parent;
//更新雙親結(jié)點(diǎn)
parent->parent = subL;
subL->parent = pparent;
//祖父結(jié)點(diǎn)是空,說(shuō)明parent是整棵樹(shù)的根結(jié)點(diǎn)
if (pparent == nullptr) {
root = subL;
}
//parent是子樹(shù)的根結(jié)點(diǎn)
else {
//這里和左單旋一樣
if (pparent->left == parent) {
pparent->left = subL;
}
else {
pparent->right = subL;
}
}
//更新平衡因子
parent->bf = subL->bf = 0;
}
(6)左右雙旋? 新結(jié)點(diǎn)插入到較高左子樹(shù)的右側(cè)。
? 首先保存初始狀態(tài)的subLR的平衡因子,后面根據(jù)它來(lái)更新其他結(jié)點(diǎn)的平衡因子。然后對(duì)以subL為根的樹(shù)進(jìn)行左單旋,對(duì)以parent為根的樹(shù)進(jìn)行右單旋。
? 最后根據(jù)保存的subLR結(jié)點(diǎn)的平衡因子進(jìn)行更新,如果BF是-1,就把parent的平衡因子更新為1,否則把subL的平衡因子更新為-1。(這里很重要,一定要跟圖片結(jié)合看)
//左右雙旋
void RotateLR(Node* parent) {
Node* subL = parent->left;
Node* subLR = subL->right;
//保存subLR的平衡因子,后面需要根據(jù)它來(lái)更新別的平衡因子
int BF = subLR->bf;
//對(duì)以subL為根結(jié)點(diǎn)的樹(shù)進(jìn)行左單旋
RotateLeft(subL);
//對(duì)以parent為根結(jié)點(diǎn)的樹(shù)進(jìn)行右單旋
RotateRight(parent);
//根據(jù)保存的平衡因子更新
//當(dāng)subLR平衡因子為-1,將parent的平衡因子更新為1
if (BF == -1)
parent->bf = 1;
//當(dāng)subLR平衡因子不是-1,將subL的平衡因子更新為-1
else
subL->bf = -1;
}
(7)右左雙旋? 新結(jié)點(diǎn)插入到較高右子樹(shù)的左側(cè)。
? 首先保存初始狀態(tài)的subRL的平衡因子,后面根據(jù)它來(lái)更新其他結(jié)點(diǎn)的平衡因子。然后對(duì)以subR為根的樹(shù)進(jìn)行右單旋,對(duì)以parent為根的樹(shù)進(jìn)行左單旋。
? 最后根據(jù)保存的subRL結(jié)點(diǎn)的平衡因子進(jìn)行更新,如果BF是-1,就把subR的平衡因子更新為1,否則把parent的平衡因子更新為-1。(跟左右雙旋一樣,一定要跟圖片結(jié)合看)
//右左雙旋
void RotateRL(Node* parent) {
Node* subR = parent->right;
Node* subRL = subR->left;
//保存subRL的平衡因子,后面根據(jù)它更新其他結(jié)點(diǎn)平衡因子
int BF = subRL->bf;
//對(duì)以subR為根的樹(shù)進(jìn)行右單旋
RotateRight(subR);
//對(duì)以parent為根的樹(shù)進(jìn)行左單旋
RotateLeft(parent);
//根據(jù)保存的subRL平衡因子的不同來(lái)進(jìn)行不同的更新。
if (BF == -1)
subR->bf = 1;
else
parent->bf = -1;
}
(8)插入函數(shù)? 插入函數(shù)首先為元素找合適的位置,同時(shí)判斷是否已經(jīng)有相同元素了。成功插入后,需要更新平衡因子,然后對(duì)平衡因子進(jìn)行檢測(cè),不符合AVL樹(shù)性質(zhì)的,需要根據(jù)情況進(jìn)行單旋或雙旋。
? 如果進(jìn)行了單旋,那么就需要繼續(xù)往上檢測(cè)平衡因子,因?yàn)閱涡罂赡軙?huì)影響上層的平衡因子。但進(jìn)行雙旋后,就可以直接返回了,因?yàn)殡p旋后已經(jīng)處理好了。
//插入函數(shù)
bool Insert(const T& _value) {
//樹(shù)為空,將新結(jié)點(diǎn)作為根結(jié)點(diǎn)即可
if (root == nullptr) {
root = new Node(_value);
return true;
}
//樹(shù)非空
Node* cur = root;
//用來(lái)保存cur的雙親結(jié)點(diǎn)
Node* parent = nullptr;
//為新結(jié)點(diǎn)的插入尋找合適的位置
//同時(shí)判斷是否樹(shù)中有相同結(jié)點(diǎn)
while (cur) {
parent = cur;
if (_value< cur->value) {
cur = cur->left;
}
else if (_value >cur->value) {
cur = cur->right;
}
else {
return false;
}
}
//構(gòu)造新結(jié)點(diǎn)
cur = new Node(_value);
//判斷是新結(jié)點(diǎn)應(yīng)該插在雙親的左邊還是右邊
if (_value< parent->value) {
parent->left = cur;
}
else {
parent->right = cur;
}
//更新新結(jié)點(diǎn)的雙親
cur->parent = parent;
//更新平衡因子
//從下往上
while (parent) {
//cur是雙親左孩子
//左子樹(shù)高度變高,平衡因子就減1
//平衡因子:右子樹(shù)高度減左子樹(shù)高度
if (cur == parent->left) {
parent->bf--;
}
//右子樹(shù)變高,平衡因子加1
else {
parent->bf++;
}
//判斷更新后的平衡因子是否滿足AVL樹(shù)
//證明parent之前只有一個(gè)孩子,而新結(jié)點(diǎn)插到另一個(gè)空著的位置了
if (parent->bf == 0) {
break;
}
//證明parent之前沒(méi)有孩子,新結(jié)點(diǎn)插入后只有一個(gè)孩子
else if (-1 == parent->bf || 1 == parent->bf) {
cur = parent;
parent = cur->parent;
}
//如下就是parent的平衡因子變成2或-2了
//需要根據(jù)圖片進(jìn)行理解
else {
//說(shuō)明parent的右子樹(shù)高
if (parent->bf == 2) {
//根據(jù)cur的平衡因子判斷是左單旋還是右左雙旋
if (cur->bf == 1)
RotateLeft(parent);
else
RotateRL(parent);
}
//parent左子樹(shù)高
else {
if (cur->bf == -1)
RotateRight(parent);
else
RotateLR(parent);
}
//使用雙旋調(diào)整后就不需要向上繼續(xù)調(diào)整了,可以直接退出循環(huán)
break;
}
}
return true;
}
(9)中序遍歷? 根據(jù)中序遍歷規(guī)則:左、根、右。進(jìn)行遍歷即可。
//中序遍歷
void InOrder() {
cout<< "中序遍歷:";
InOrder(root);
cout<< endl;
}
//中序遍歷內(nèi)部實(shí)現(xiàn)
void InOrder(Node* root) {
if (root) {
//首先遞歸遍歷左子樹(shù),然后打印根結(jié)點(diǎn),最后遞歸遍歷右子樹(shù)
InOrder(root->left);
cout<< root->value<< " ";
InOrder(root->right);
}
}
(10)析構(gòu)函數(shù)? 析構(gòu)函數(shù)沒(méi)有參數(shù),所以需要單獨(dú)實(shí)現(xiàn)一個(gè)銷(xiāo)毀函數(shù)。先遞歸刪除左右子樹(shù),最后刪除根結(jié)點(diǎn)。
//析構(gòu)函數(shù)
//因?yàn)槲鰳?gòu)函數(shù)無(wú)參數(shù),需要單獨(dú)定義銷(xiāo)毀函數(shù)
~AVLTree() {
Destroy(root);
}
//銷(xiāo)毀函數(shù)(和二叉搜索樹(shù)一模一樣)
void Destroy(Node*& root) {
if (root) {
//遞歸銷(xiāo)毀根的左子樹(shù)和右子樹(shù)然后刪除根結(jié)點(diǎn)
Destroy(root->left);
Destroy(root->right);
delete root;
root = nullptr;
}
}
(11)驗(yàn)證函數(shù)? 驗(yàn)證函數(shù)通過(guò)高度函數(shù)獲得左右子樹(shù)的高度,進(jìn)而計(jì)算出高度差,同時(shí)將高度差的絕對(duì)值和高度差與平衡因子的比較結(jié)合在一起判斷這棵樹(shù)是否滿足AVL樹(shù)的性質(zhì)。
//驗(yàn)證函數(shù)
bool IsAVLTree() {
return IsAVLTree(root);
}
//驗(yàn)證函數(shù)主體實(shí)現(xiàn)
bool IsAVLTree(Node* root) {
if (root == nullptr) {
return true;
}
//計(jì)算根結(jié)點(diǎn)左右子樹(shù)高度
int leftHeight = Height(root->left);
int rightHeight = Height(root->right);
//根據(jù)平衡因子絕對(duì)值 和 子樹(shù)高度差與根結(jié)點(diǎn)平衡因子是否相等
//判斷是否滿足AVL樹(shù)性質(zhì)
if (abs(rightHeight - leftHeight) >1 || rightHeight - leftHeight != root->bf) {
cout<< "結(jié)點(diǎn)值:"<< root->value<< " 結(jié)點(diǎn)平衡因子:"<< root->bf<< endl;
return false;
}
//遞歸調(diào)用
return IsAVLTree(root->left) && IsAVLTree(root->right);
}
//求高度函數(shù)
int Height(Node* root) {
if (root == nullptr) {
return 0;
}
int leftHeight = Height(root->left);
int rightHeight = Height(root->right);
return leftHeight >rightHeight ? (leftHeight + 1) : (rightHeight + 1);
}
4.AVL樹(shù)的整體實(shí)現(xiàn)與驗(yàn)證
? (1)AVL樹(shù)的整體實(shí)現(xiàn)#include "iostream"
using namespace std;
//結(jié)點(diǎn)類
templatestruct AVLTreeNode {
AVLTreeNode* left;//左孩子指針
AVLTreeNode* right;//右孩子指針
AVLTreeNode* parent;//雙親指針
T value;//存儲(chǔ)元素的變量
int bf;//平衡因子
//用傳入的值構(gòu)造結(jié)點(diǎn)
AVLTreeNode(const T& _value = T())
:left(nullptr)
,right(nullptr)
,parent(nullptr)
,value(_value)
,bf(0)
{}
};
//AVL樹(shù)類
templateclass AVLTree {
//給結(jié)點(diǎn)類起別名,方便使用
typedef AVLTreeNodeNode;
private:
//指向根結(jié)點(diǎn)的結(jié)點(diǎn)指針
Node* root;
public:
//無(wú)參構(gòu)造
//讓根結(jié)點(diǎn)指針指向空即可
AVLTree()
:root(nullptr)
{}
//析構(gòu)函數(shù)
//因?yàn)槲鰳?gòu)函數(shù)無(wú)參數(shù),需要單獨(dú)定義銷(xiāo)毀函數(shù)
~AVLTree() {
Destroy(root);
}
//銷(xiāo)毀函數(shù)(和二叉搜索樹(shù)一模一樣)
void Destroy(Node*& root) {
if (root) {
//遞歸銷(xiāo)毀根的左子樹(shù)和右子樹(shù)然后刪除根結(jié)點(diǎn)
Destroy(root->left);
Destroy(root->right);
delete root;
root = nullptr;
}
}
//左單旋
//以下過(guò)程建議搭配圖片理解
void RotateLeft(Node* parent) {
//這里的指針設(shè)計(jì)在講解旋轉(zhuǎn)的時(shí)候已經(jīng)解釋過(guò)
Node* subR = parent->right;
Node* subRL = subR->left;
//用pparent(祖父結(jié)點(diǎn))來(lái)保存parent結(jié)點(diǎn)的雙親
//因?yàn)閜arent并不一定是整棵樹(shù)的根,還有可能只是一棵子樹(shù)的根
Node* pparent = parent->parent;
//第一步:首先讓雙親的右孩子指針指向subRL
parent->right = subRL;
//之所以要判斷是因?yàn)閟ubRL結(jié)點(diǎn)可能不存在
if (subRL)
//更新雙親
subRL->parent = parent;
//第二步:讓subR的左孩子指針指向parent
subR->left = parent;
//不要忘記給這些結(jié)點(diǎn)更新它們的雙親
parent->parent = subR;
subR->parent = pparent;
//祖父結(jié)點(diǎn)是空,說(shuō)明parent結(jié)點(diǎn)是整棵樹(shù)的根結(jié)點(diǎn)
if (pparent == nullptr) {
root = subR;
}
//說(shuō)明parent是子樹(shù)的根結(jié)點(diǎn)
else {
//原本祖父結(jié)點(diǎn)的孩子是parent
//經(jīng)過(guò)旋轉(zhuǎn)他的孩子變成了subR
//需要判斷parent是祖父結(jié)點(diǎn)的左孩子還是右孩子,然后讓對(duì)應(yīng)指針指向subR結(jié)點(diǎn)
if (pparent->left == parent) {
pparent->left = subR;
}
else {
pparent->right = subR;
}
}
//更新平衡因子
parent->bf = subR->bf = 0;
}
//右單旋
void RotateRight(Node* parent) {
Node* subL = parent->left;
Node* subLR = subL->right;
//祖父結(jié)點(diǎn)
Node* pparent = parent->parent;
//第一步:讓parent的左孩子指針指向subLR
parent->left = subLR;
//之所以要判斷是因?yàn)閟ubLR結(jié)點(diǎn)可能不存在
if (subLR)
subLR->parent = parent;
//第二步:讓subL的右孩子指針指向parent
subL->right = parent;
//更新雙親結(jié)點(diǎn)
parent->parent = subL;
subL->parent = pparent;
//祖父結(jié)點(diǎn)是空,說(shuō)明parent是整棵樹(shù)的根結(jié)點(diǎn)
if (pparent == nullptr) {
root = subL;
}
//parent是子樹(shù)的根結(jié)點(diǎn)
else {
//這里和左單旋一樣
if (pparent->left == parent) {
pparent->left = subL;
}
else {
pparent->right = subL;
}
}
//更新平衡因子
parent->bf = subL->bf = 0;
}
//左右雙旋
void RotateLR(Node* parent) {
Node* subL = parent->left;
Node* subLR = subL->right;
//保存subLR的平衡因子,后面需要根據(jù)它來(lái)更新別的平衡因子
int BF = subLR->bf;
//對(duì)以subL為根結(jié)點(diǎn)的樹(shù)進(jìn)行左單旋
RotateLeft(subL);
//對(duì)以parent為根結(jié)點(diǎn)的樹(shù)進(jìn)行右單旋
RotateRight(parent);
//根據(jù)保存的平衡因子更新
//當(dāng)subLR平衡因子為-1,將parent的平衡因子更新為1
if (BF == -1)
parent->bf = 1;
//當(dāng)subLR平衡因子不是-1,將subL的平衡因子更新為-1
else
subL->bf = -1;
}
//右左雙旋
void RotateRL(Node* parent) {
Node* subR = parent->right;
Node* subRL = subR->left;
//保存subRL的平衡因子,后面根據(jù)它更新其他結(jié)點(diǎn)平衡因子
int BF = subRL->bf;
//對(duì)以subR為根的樹(shù)進(jìn)行右單旋
RotateRight(subR);
//對(duì)以parent為根的樹(shù)進(jìn)行左單旋
RotateLeft(parent);
//根據(jù)保存的subRL平衡因子的不同來(lái)進(jìn)行不同的更新。
if (BF == -1)
subR->bf = 1;
else
parent->bf = -1;
}
//插入函數(shù)
bool Insert(const T& _value) {
//樹(shù)為空,將新結(jié)點(diǎn)作為根結(jié)點(diǎn)即可
if (root == nullptr) {
root = new Node(_value);
return true;
}
//樹(shù)非空
Node* cur = root;
//用來(lái)保存cur的雙親結(jié)點(diǎn)
Node* parent = nullptr;
//為新結(jié)點(diǎn)的插入尋找合適的位置
//同時(shí)判斷是否樹(shù)中有相同結(jié)點(diǎn)
while (cur) {
parent = cur;
if (_value< cur->value) {
cur = cur->left;
}
else if (_value >cur->value) {
cur = cur->right;
}
else {
return false;
}
}
//構(gòu)造新結(jié)點(diǎn)
cur = new Node(_value);
//判斷是新結(jié)點(diǎn)應(yīng)該插在雙親的左邊還是右邊
if (_value< parent->value) {
parent->left = cur;
}
else {
parent->right = cur;
}
//更新新結(jié)點(diǎn)的雙親
cur->parent = parent;
//更新平衡因子
//從下往上
while (parent) {
//cur是雙親左孩子
//左子樹(shù)高度變高,平衡因子就減1
//平衡因子:右子樹(shù)高度減左子樹(shù)高度
if (cur == parent->left) {
parent->bf--;
}
//右子樹(shù)變高,平衡因子加1
else {
parent->bf++;
}
//判斷更新后的平衡因子是否滿足AVL樹(shù)
//證明parent之前只有一個(gè)孩子,而新結(jié)點(diǎn)插到另一個(gè)空著的位置了
if (parent->bf == 0) {
break;
}
//證明parent之前沒(méi)有孩子,新結(jié)點(diǎn)插入后只有一個(gè)孩子
else if (-1 == parent->bf || 1 == parent->bf) {
cur = parent;
parent = cur->parent;
}
//如下就是parent的平衡因子變成2或-2了
//需要根據(jù)圖片進(jìn)行理解
else {
//說(shuō)明parent的右子樹(shù)高
if (parent->bf == 2) {
//根據(jù)cur的平衡因子判斷是左單旋還是右左雙旋
if (cur->bf == 1)
RotateLeft(parent);
else
RotateRL(parent);
}
//parent左子樹(shù)高
else {
if (cur->bf == -1)
RotateRight(parent);
else
RotateLR(parent);
}
//使用雙旋調(diào)整后就不需要向上繼續(xù)調(diào)整了,可以直接退出循環(huán)
break;
}
}
return true;
}
//中序遍歷
void InOrder() {
cout<< "中序遍歷:";
InOrder(root);
cout<< endl;
}
//中序遍歷內(nèi)部實(shí)現(xiàn)
void InOrder(Node* root) {
if (root) {
//首先遞歸遍歷左子樹(shù),然后打印根結(jié)點(diǎn),最后遞歸遍歷右子樹(shù)
InOrder(root->left);
cout<< root->value<< " ";
InOrder(root->right);
}
}
//驗(yàn)證函數(shù)
bool IsAVLTree() {
return IsAVLTree(root);
}
//驗(yàn)證函數(shù)主體實(shí)現(xiàn)
bool IsAVLTree(Node* root) {
if (root == nullptr) {
return true;
}
//計(jì)算根結(jié)點(diǎn)左右子樹(shù)高度
int leftHeight = Height(root->left);
int rightHeight = Height(root->right);
//根據(jù)平衡因子絕對(duì)值 和 子樹(shù)高度差與根結(jié)點(diǎn)平衡因子是否相等
//判斷是否滿足AVL樹(shù)性質(zhì)
if (abs(rightHeight - leftHeight) >1 || rightHeight - leftHeight != root->bf) {
cout<< "結(jié)點(diǎn)值:"<< root->value<< " 結(jié)點(diǎn)平衡因子:"<< root->bf<< endl;
return false;
}
//遞歸調(diào)用
return IsAVLTree(root->left) && IsAVLTree(root->right);
}
//求高度函數(shù)
int Height(Node* root) {
if (root == nullptr) {
return 0;
}
int leftHeight = Height(root->left);
int rightHeight = Height(root->right);
return leftHeight >rightHeight ? (leftHeight + 1) : (rightHeight + 1);
}
};
int main() {
int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTreeavl;
for (int i = 0; i< 10; i++) {
avl.Insert(arr[i]);
avl.InOrder();
}
if (avl.IsAVLTree()) {
cout<< "是AVL樹(shù)"<< endl;
}
else {
cout<< "不是AVL樹(shù)"<< endl;
}
}
(2)AVL樹(shù)的驗(yàn)證? 上文代碼的運(yùn)行結(jié)果如下:符合AVL樹(shù)的性質(zhì)。同時(shí)中序遍歷也是一個(gè)升序的序列。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
分享標(biāo)題:數(shù)據(jù)結(jié)構(gòu)(C++):AVL樹(shù)實(shí)現(xiàn)篇-創(chuàng)新互聯(lián)
網(wǎng)站鏈接:http://aaarwkj.com/article16/icidg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開(kāi)發(fā)、App開(kāi)發(fā)、服務(wù)器托管、網(wǎng)站設(shè)計(jì)公司、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容
營(yíng)銷(xiāo)型網(wǎng)站建設(shè)知識(shí)