真c++ 从二叉树到红黑树(4)之二叉平衡搜索树AVL
??此文章為從二叉樹到紅黑樹系列文章的第四節(jié),主要介紹介紹二叉平衡搜索樹AVL,當你理解了AVL,紅黑樹你就理解了一半了!
文章目錄
- 一、前面文章鏈接~(點擊右邊波浪線可以返回目錄)
- 二、由BST引入BBST~
- 1.理想平衡~
- 2.適度平衡~
- 3.等價變換~
- 三、AVL樹的定義~
- 1.AVL樹的適度平衡~
- 2.AVL樹的平衡因子~
- 四、AVL類~
- (一)定義變量和接口~
- 1.利用已有的成員變量~
- 2.需要的接口~
- 3.重要輔助函數(shù)~
- 4.AVL.h~
- (二)判斷是否失衡~
- (三)AVL的失衡與重平衡~
- 1.因插入引起的失衡~
- (1)LL型失衡~
- (2)RR型失衡~
- (3)LR型失衡~
- (4)RL型失衡~
- 2.因刪除引起的失衡~
- (1)LL型失衡~
- (2)RR型失衡~
- (3)LR型失衡~
- (4)RL型失衡~
- 3.失衡情況總結(jié)~
- 4.統(tǒng)一重平衡算法~
- (1)萬能的connect 3+4算法~
- connece34代碼~
- (2)萬能的rotateAt算法~
- rotateAt代碼~
- (四)AVL的插入~
- 插入代碼~
- tallerChild函數(shù)~
- 效率分析~
- (五)AVL的刪除~
- 刪除代碼~
- 效率分析~
- 五、完整AVL.h~
- 六、測試代碼~
- 1.插入測試代碼~
- 2.插入測試圖示~
- 3.刪除測試代碼~
- 4.刪除測試圖示~
- 七、后序文章鏈接~
一、前面文章鏈接~(點擊右邊波浪線可以返回目錄)
??在閱讀本文前,強烈建議你看下前面的文章的目錄、前言以及基本介紹,否則你無法理解后面的內(nèi)容。鏈接如下:
二、由BST引入BBST~
??在介紹AVL之前,我們先來了解下什么叫二叉平衡搜索樹(BBST)以及為什么要引入BBST。
1.理想平衡~
??既然二叉搜索樹的性能主要取決于高度,故在節(jié)點數(shù)目固定的前提下,應(yīng)盡可能地降低高度。
??若樹高恰好為?log2n?(二叉樹的最低高度),則稱作理想平衡樹。如滿二叉樹和完全二叉樹。
??遺憾的是,完全二叉樹 “葉節(jié)點只能出現(xiàn)于最底部兩層” 的限制過于苛刻。所以要制定某種寬松的標準來實現(xiàn)適度平衡。
2.適度平衡~
??將樹高限制為“漸進地不超過log2n”。如AVL,RedBlack等。
3.等價變換~
??若兩棵樹的中序遍歷序列相同,則彼此等價。
三、AVL樹的定義~
1.AVL樹的適度平衡~
左右兄弟節(jié)點的高度相差不過1。2.AVL樹的平衡因子~
??由適度平衡的定義不難得知,要保持一顆AVL樹的平衡,必須使其左右子樹的高度相差不超過1.故用數(shù)學的方式可以理解為:
平衡因子(BalanceFactor)=左子樹高度-右子樹高度 平衡因子的絕對值不超過1。四、AVL類~
(一)定義變量和接口~
1.利用已有的成員變量~
??由于AVL樹屬于BST的一種,因此AVL樹可以繼承自BST,而BST又繼承自BinTree(二叉樹),而在此前(本系列文章第2節(jié),第3節(jié)中),我們已經(jīng)擁有了以下成員變量,因此不需要額外給AVL定義變量。
BinNodePtr _hot;//"命中節(jié)點"的"父親"int _size;//二叉樹的規(guī)模 BinNodePtr _root;//二叉樹的樹根2.需要的接口~
??由于在BST中,我們已經(jīng)定義了查找search算法,因此,不需要給AVL重新寫查找算法,只需要對插入和刪除算法進行重寫既可,AVL和BST的插入和刪除算法,本質(zhì)上沒有區(qū)別,有區(qū)別的僅僅是插入和刪除之后的調(diào)整,而這種調(diào)整正是AVL與BST的最大差別所在。
在樹中插入一個節(jié)點insert 在樹中刪除一個節(jié)點remove3.重要輔助函數(shù)~
??在AVL中,引入了一個平衡因子的概念,所以需要一個函數(shù),來判斷此時的AVL樹是否平衡。
判斷是否平衡的函數(shù)IsAvlBalanced??并且還需要一個重要的輔助函數(shù)來得到當前節(jié)點的最高的那個孩子節(jié)點(這個函數(shù)在描述AVL的插入刪除算法時就會發(fā)揮作用)
得到該節(jié)點更高的孩子tallerChild??此外,不要忘了在BST中遺留的兩個函數(shù),在介紹重平衡時,也會描述這兩個算法的作用
3+4重構(gòu) connect34 對該節(jié)點及其父親、祖父做統(tǒng)一旋轉(zhuǎn)調(diào)整rotateAt4.AVL.h~
template<typename T = int> class AVL :public BST<T> {protected:using BinNodePtr = BinNode<T>*;public:BinNodePtr insert(const T& data)override;//插入(重寫)bool remove(const T& data)override;//刪除(重寫)protected:static constexpr bool IsAvlBalanced(const BinNodePtr& x) {//判斷是否平衡int BalanceFactor = stature(x->_lchild) - stature(x->_rchild);return (-2 < BalanceFactor && BalanceFactor < 2);}static BinNodePtr& tallerChild(BinNode<T>*& x);//更高的孩子 };//class AVL(二)判斷是否失衡~
??借助AVL樹的平衡因子的概念,不難寫出判斷是否平衡的代碼。其中stature是在本系列文章第一部分定義的得出當前節(jié)點高度的全局靜態(tài)函數(shù)。
static constexpr bool IsAvlBalanced(const BinNodePtr& x) {//判斷是否平衡int BalanceFactor = stature(x->_lchild) - stature(x->_rchild);return (-2 < BalanceFactor && BalanceFactor < 2); }??AVL樹失衡時,當且僅當IsAvlBalanced函數(shù)的返回值為false。
(三)AVL的失衡與重平衡~
??當AVL的平衡因子不再滿足平衡條件時,AVL就會發(fā)生失衡,而失衡無外乎就四種失衡情況,接下來,我們通過具體的例子來看看因為插入和刪除引起的AVL的失衡。
1.因插入引起的失衡~
(1)LL型失衡~
??下圖5 3由于2的插入,導致5的失衡,所以進行重平衡,即將3提升為根節(jié)點,5作為3的右孩子。
(2)RR型失衡~
??下圖2 3由于4的插入,導致2的失衡,所以進行重平衡,即將3提升為根節(jié)點,2作為3的左孩子。
(3)LR型失衡~
??下圖3 1由于2的插入,導致3的失衡,所以進行重平衡,即將2提升為根節(jié)點,1作為2的左孩子,3作為2的右孩子。
(4)RL型失衡~
??下圖3 5由于4的插入,導致3的失衡,所以進行重平衡,即將4提升為根節(jié)點,3作為4的左孩子,5作為4的右孩子。
2.因刪除引起的失衡~
(1)LL型失衡~
??下圖3 2 5 1由于5的插入,導致3的失衡,所以進行重平衡,即將2提升為根節(jié)點,3作為2的右孩子。
(2)RR型失衡~
??下圖3 2 4 5由于2的刪除,導致3的失衡,所以進行重平衡,即將4提升為根節(jié)點,3作為4的左孩子。
(3)LR型失衡~
??下圖4 2 5 3由于5的刪除,導致4的失衡,所以進行重平衡,即將3提升為根節(jié)點,2作為3的左孩子,4作為3的右孩子。
(4)RL型失衡~
??下圖2 1 5 3由于1的刪除,導致2的失衡,所以進行重平衡,即將3提升為根節(jié)點,2作為3的左孩子,5作為3的右孩子。
3.失衡情況總結(jié)~
??從上面插入的四種情況,以及刪除的四種情況可以看出,無論是插入還是刪除,其失衡之后的形狀都是一樣的,所以調(diào)整的方式也是一樣的!
??因此,不管是插入還是刪除,我們都可以采用同樣的調(diào)整方式。
??在很多教程上,都是用單旋(LL 或RR型失衡 只旋轉(zhuǎn)一次)和雙旋(LR或RL型失衡 要旋轉(zhuǎn)兩次)來處理失衡的情況,如果你看過其他的教程,那么你必然對下面的圖有所熟悉。當然,你不熟悉,也沒關(guān)系,我們只看這四種情況對應(yīng)的結(jié)果。
??可以發(fā)現(xiàn),無論是單旋還是雙旋,其調(diào)整之后的形狀毫無例外,都是這種結(jié)構(gòu)。
??所以,我們可以只關(guān)注結(jié)果,不關(guān)心過程,無論哪種失衡情況,其調(diào)整之后的結(jié)構(gòu)都是上面這種結(jié)構(gòu)。因此就可以寫一個通用的調(diào)整函數(shù),來專門負責失衡之后的調(diào)整。
4.統(tǒng)一重平衡算法~
??幸運的是,鄧老師已經(jīng)給出了解決方案。有了這種方法,你就再也不必為到底該左旋還是右旋而苦惱,也不必去記那種繁瑣的左右旋算法。
(1)萬能的connect 3+4算法~
??將上面的四種情況,統(tǒng)一簡化成下面圖中的形式。無論是那種調(diào)整方式,最終都必然調(diào)整為這種形式。
??實際上,這一理解涵蓋了所有的單旋和雙旋情況。相應(yīng)的重構(gòu)過程,僅涉及局部的三個節(jié)點及其四棵子樹,故稱作“3 + 4”重構(gòu)。
??到這里,我相信你已經(jīng)理解了在第三部分BST中所定義的connect34函數(shù)的作用了。
connece34代碼~
??接下來只要將對應(yīng)的節(jié)點按順序填入下面這個函數(shù),就能實現(xiàn)重平衡。同時注意更新高度。
??在BST.h中定義的成員函數(shù)connect34,注意返回值為調(diào)整之后的根節(jié)點。
template<typename T> BinNode<T>* BST<T>::connect34(BinNode<T>* a, BinNode<T>* b, BinNode<T>* c,BinNode<T>* T1, BinNode<T>* T2, BinNode<T>* T3, BinNode<T>* T4) {a->_lchild = T1; if (T1)T1->_parent = a;a->_rchild = T2; if (T2)T2->_parent = a; this->updateHeight(a);c->_lchild = T3; if (T3)T3->_parent = c;c->_rchild = T4; if (T4)T4->_parent = c; this->updateHeight(c);b->_lchild = a; a->_parent = b;b->_rchild = c; c->_parent = b; this->updateHeight(b);return b; }??下一步,問題就在于,如何確定 a b c T1 T2 T3 T4的順序?
(2)萬能的rotateAt算法~
??觀察上面四種失衡情況,無論是哪種失衡情況,其失衡時,都必然涉及3個節(jié)點的位置變換。
??并且這三個節(jié)點,也一定為祖孫三代的關(guān)系。
??因此,不妨設(shè)最小的那個節(jié)點為v,其父親為p,其祖父為g。分四種情況,將v p g以及對應(yīng)的孩子,放入connect34函數(shù)里面進行重平衡。
rotateAt代碼~
??在BST.h中,我們定義了rotateAt算法,下面為其具體實現(xiàn)。對需要調(diào)整的節(jié)點,分四種情況,借助connect34函數(shù),進行調(diào)整7個節(jié)點的相對位置。
template<typename T> BinNode<T>* BST<T>::rotateAt(BinNode<T>* v) //返回調(diào)整后局部子樹根節(jié)點的位置 {if (v == nullptr) {printf("Error!"); exit(0);}//設(shè)定v的父親與祖父//視v、p和g相對位置分四種情況BinNode<T>* p = v->_parent; BinNode<T>* g = p->_parent;if (IsLChild(p)) {//Lif (IsLChild(v)) {//LLp->_parent = g->_parent;//向上連接return connect34(v, p, g, v->_lchild, v->_rchild, p->_rchild, g->_rchild);}else {//LRv->_parent = g->_parent;//向上連接return connect34(p, v, g, p->_lchild, v->_lchild, v->_rchild, g->_rchild);}}else {//Rif (IsRChild(v)) {//RRp->_parent = g->_parent;//向上連接return connect34(g, p, v, g->_lchild, p->_lchild, v->_lchild, v->_rchild);}else {//RLv->_parent = g->_parent;//向上連接return connect34(g, v, p, g->_lchild, v->_lchild, v->_rchild, p->_rchild);}} }讀者可以繼續(xù)通過這兩個圖來細細體會。
(四)AVL的插入~
??在理解了AVL的重平衡原理之后,AVL的插入和刪除就也很容易理解了。
在插入新的節(jié)點的時候,有一個現(xiàn)象,就是無論如何:1. 新節(jié)點的父親節(jié)點,一定不可能失衡; 2. 并且失衡的節(jié)點一定為該新節(jié)點的祖先節(jié)點; 3. 一旦其有一個祖先恢復了平衡,整顆AVL樹必將恢復平衡。插入代碼~
??像往常的樹的插入一樣,在插入之前,我們需要進行查找操作,如果存在這個節(jié)點,就不插入,如果不存在這個節(jié)點,就插入新的節(jié)點,并且得益于_hot節(jié)點(見第三部分BST中_hot節(jié)點的作用),我們能很快的將節(jié)點插入正確的位置,同時也更新了對應(yīng)的父子節(jié)點指針。
??接下來就按照AVL插入新節(jié)點的性質(zhì),來寫出下面的代碼。并且在此處我們要用到FromParentTo算法(見第二部分BinTree),此算法的作用是獲取當前節(jié)點的父親的孩子的引用,即獲取當前節(jié)點的引用,哪怕當前節(jié)點里面的內(nèi)容發(fā)生了變化,其作為指針的本身的地址不會發(fā)生變化,只是指針所指的地址發(fā)生了變化。
template<typename T> BinNode<T>* AVL<T>::insert(const T& data){//無論data在不在子樹中,返回值的_data均為dataBinNode<T>*& x = this->search(data);if (x)return x;x = new BinNode<T>(data,this->_hot);//創(chuàng)建新節(jié)點this->_size++;//更新規(guī)模for (BinNode<T>* g = this->_hot; g; g = g->_parent) {if (!IsAvlBalanced(g)) {//如果失衡//必須要提前將地址取好,不然g的里面的數(shù)據(jù)發(fā)生變動,就會造成指針亂指。BinNode<T>*& parent_child_Address = this->FromParentTo(g);parent_child_Address= this->rotateAt(tallerChild(tallerChild(g)));/*首先調(diào)用tallerChild函數(shù),來確定到底旋轉(zhuǎn)哪一個g的孫子節(jié)點(選擇g的最高孩子的最高孩子作為旋轉(zhuǎn)節(jié)點)*在旋轉(zhuǎn)過程中,對g的孩子,g的孫子的左右旋情況,分四種情況進行判斷。并利用connect34函數(shù),做快速重構(gòu)*從而實現(xiàn)將失衡的g恢復正常平衡。并且rotateAt的返回值,就是重構(gòu)后的對應(yīng)子樹的根節(jié)點的位置,把這個返回值*作為原來g的父親的孩子,即重新連接重構(gòu)后的子樹與原來的子樹。并且在connect34過程中,對樹的高度也進行了更新。所以不必再度更新。*/break;//一旦發(fā)生了重構(gòu),就不需要對后續(xù)的祖先進行高度更新和重構(gòu)了。}else {this->updateHeight(g);//未失衡,只需更新g的高度,不需要更新所有高度。}}return x; }??并且在插入算法中,我們用到了tallerChild函數(shù),這個函數(shù)是獲取當前節(jié)點的最高孩子節(jié)點。我們需要用這個函數(shù),來獲取傳到rotateAt函數(shù)里面節(jié)點。
tallerChild函數(shù)~
??顧名思義,獲取當前節(jié)點的最高的孩子。
/*期望c++20可以支持,auto compareResult = stature(x->_lchild) <=> stature(x->_rchild);*/ template<typename T> inline BinNode<T>*& AVL<T>::tallerChild(BinNode<T>*& x){int lHeight = stature(x->_lchild);int rHeight = stature(x->_rchild);if (lHeight > rHeight) {return x->_lchild;}else if (lHeight < rHeight) {return x->_rchild;}else {//如果左右高度相等,則按父親是左孩子還是右孩子,來返回對應(yīng)的左右孩子return (IsLChild(x) ? x->_lchild : x->_rchild);} }效率分析~
??該算法首先按照二叉搜索樹的常規(guī)算法,在O(logn)時間內(nèi)插入新節(jié)點x。
??既然原樹是平衡的,故至多檢查O(logn)個節(jié)點即可確定失衡節(jié)點;如有必要,只需要進行一次重構(gòu),即可使局部乃至全樹恢復平衡。
??由此可見,AVL樹的節(jié)點插入操作可以在O(logn)時間內(nèi)完成。
(五)AVL的刪除~
在刪除新的節(jié)點的時候,有一個現(xiàn)象,就是無論如何: 1. 第一個失衡的節(jié)點,可能是父親節(jié)點。 2. 每一次失衡,都必然只有一個祖先失衡,失衡的也只可能是祖先節(jié)點,不可能多個祖先同時失衡 3. 一個祖先修復平衡后,可能接下來導致下一個祖先也失衡,極端條件下,可能要重平衡多次。刪除代碼~
??AVL的刪除的大體步驟跟BST的刪除十分類似,只是刪除之后,要進行修復平衡。因此需要借助在第三部分BST中定義的removeAt刪除靜態(tài)函數(shù)(建議理解了這個函數(shù)再來看AVL的刪除)。
??AVL刪除的重平衡算法和插入的重平衡算法類似,只是對于祖先節(jié)點處理方式不一樣,在插入算法中,一旦一個祖先恢復平衡,其他祖先就不需要進行平衡的檢測了。而刪除算法中,哪怕這個節(jié)點恢復了平衡,也要對其父親進行平衡的檢測。
??只有當前節(jié)點是平衡的,并且高度沒有發(fā)生變化時,重平衡才大功告成。
template<typename T> bool AVL<T>::remove(const T& data) {BinNode<T>*& x = this->search(data);if (!x)return false;//如果不存在,返回falseremoveAt(x, this->_hot);//刪除此節(jié)點,并更新_hot值this->_size--;//更新規(guī)模for (BinNode<T>* g = this->_hot; g; g = g->_parent) {if (!IsAvlBalanced(g)) {//如果失衡BinNode<T>*& parent_child_Address = this->FromParentTo(g);//先記錄之前的父親節(jié)點的孩子指針parent_child_Address = this->rotateAt(tallerChild(tallerChild(g)));//將孩子指針指向新的子樹根節(jié)點g = parent_child_Address;//更新g,g此時必然是處于平衡狀態(tài)}else {int gHeight = g->_height;if (gHeight == this->updateHeight(g)) {//沒失衡也要檢查高度,祖先高度沒變,則后序祖先也不需要進行更新。break;} }}return true;//刪除成功 }效率分析~
??較之插入操作,刪除操作可能需在重平衡方面多花費一些時間。不過,既然需做重平衡的節(jié)點都是被刪除節(jié)點的祖先,故重平衡過程累計只需不過O(logn)時間。
??綜合各方面的消耗,AVL樹的節(jié)點刪除操作總體的時間復雜度依然是O(logn)。
五、完整AVL.h~
#pragma once #include "BST.h"namespace mytree {template<typename T = int>class AVL :public BST<T> {protected:using BinNodePtr = BinNode<T>*;public:BinNodePtr insert(const T& data)override;//插入(重寫)bool remove(const T& data)override;//刪除(重寫)protected:static constexpr bool IsAvlBalanced(const BinNodePtr& x) {//判斷是否平衡int BalanceFactor = stature(x->_lchild) - stature(x->_rchild);return (-2 < BalanceFactor && BalanceFactor < 2);}static BinNodePtr& tallerChild(BinNode<T>*& x);//更高的孩子};//class AVLtemplate<typename T>BinNode<T>* AVL<T>::insert(const T& data){//無論data在不在子樹中,返回值的_data均為dataBinNode<T>*& x = this->search(data);if (x)return x;x = new BinNode<T>(data,this->_hot);//創(chuàng)建新節(jié)點this->_size++;//更新規(guī)模for (BinNode<T>* g = this->_hot; g; g = g->_parent) {if (!IsAvlBalanced(g)) {//如果失衡//必須要提前將地址取好,不然g的里面的數(shù)據(jù)發(fā)生變動,就會造成指針亂指。BinNode<T>*& parent_child_Address = this->FromParentTo(g);parent_child_Address= this->rotateAt(tallerChild(tallerChild(g)));/*首先調(diào)用tallerChild函數(shù),來確定到底旋轉(zhuǎn)哪一個g的孫子節(jié)點(選擇g的最高孩子的最高孩子作為旋轉(zhuǎn)節(jié)點)*在旋轉(zhuǎn)過程中,對g的孩子,g的孫子的左右旋情況,分四種情況進行判斷。并利用connect34函數(shù),做快速重構(gòu)*從而實現(xiàn)將失衡的g恢復正常平衡。并且rotateAt的返回值,就是重構(gòu)后的對應(yīng)子樹的根節(jié)點的位置,把這個返回值*作為原來g的父親的孩子,即重新連接重構(gòu)后的子樹與原來的子樹。并且在connect34過程中,對樹的高度也進行了更新。所以不必再度更新。*/break;//一旦發(fā)生了重構(gòu),就不需要對后續(xù)的祖先進行高度更新和重構(gòu)了。}else {this->updateHeight(g);//未失衡,只需更新g的高度,不需要更新所有高度。}}return x;}template<typename T>bool AVL<T>::remove(const T& data){BinNode<T>*& x = this->search(data);if (!x)return false;//如果不存在,返回falseremoveAt(x, this->_hot);//刪除此節(jié)點,并更新_hot值this->_size--;//更新規(guī)模for (BinNode<T>* g = this->_hot; g; g = g->_parent) {if (!IsAvlBalanced(g)) {//如果失衡BinNode<T>*& parent_child_Address = this->FromParentTo(g);//先記錄之前的父親節(jié)點的孩子指針parent_child_Address = this->rotateAt(tallerChild(tallerChild(g)));//將孩子指針指向新的子樹根節(jié)點g = parent_child_Address;//更新g,g此時必然是處于平衡狀態(tài)}else {int gHeight = g->_height;if (gHeight == this->updateHeight(g)) {//沒失衡也要檢查高度,祖先高度沒變,則后序祖先也不需要進行更新。break;} }}return true;//刪除成功}/*期望c++20可以支持,auto compareResult = stature(x->_lchild) <=> stature(x->_rchild);*/template<typename T>inline BinNode<T>*& AVL<T>::tallerChild(BinNode<T>*& x){int lHeight = stature(x->_lchild);int rHeight = stature(x->_rchild);if (lHeight > rHeight) {return x->_lchild;}else if (lHeight < rHeight) {return x->_rchild;}else {//如果左右高度相等,則按父親是左孩子還是右孩子,來返回對應(yīng)的左右孩子return (IsLChild(x) ? x->_lchild : x->_rchild);}}}//namespace mytree六、測試代碼~
1.插入測試代碼~
#include<iostream> #include "AVL.h" using namespace std; using namespace mytree;template<typename BinNodePtr> void visitprint(BinNodePtr x) {cout << x->_data; }int main() {AVL<int>* avl = new AVL<int>;for (int i = 0; i < 10; ++i) {avl->insert(i);}avl->travLevel(visitprint<BinNode<int>*>);cout << endl;avl->travPre(visitprint<BinNode<int>*>);cout << endl;avl->travIn(visitprint<BinNode<int>*>);cout << endl;avl->travPost(visitprint<BinNode<int>*>);cout << endl;delete avl;return 0; } 3170258469 3102754689 0123456789 02146598732.插入測試圖示~
3.刪除測試代碼~
#include<iostream> #include "AVL.h" using namespace std; using namespace mytree;template<typename BinNodePtr> void visitprint(BinNodePtr x) {cout << x->_data; }int main() {AVL<int>* avl = new AVL<int>;for (int i = 0; i < 10; ++i) {avl->insert(i);}avl->travLevel(visitprint<BinNode<int>*>);cout << endl;avl->travPre(visitprint<BinNode<int>*>);cout << endl;avl->travIn(visitprint<BinNode<int>*>);cout << endl;avl->travPost(visitprint<BinNode<int>*>);cout << endl<<endl;for (int i = 0; i < 10; ++i) {avl->remove(i);avl->travIn(visitprint<BinNode<int>*>);cout << endl;}delete avl;return 0; } 3170258469 3102754689 0123456789 0214659873123456789 23456789 3456789 456789 56789 6789 789 89 94.刪除測試圖示~
七、后序文章鏈接~
學一個東西,不知道其道理,不高明!
總結(jié)
以上是生活随笔為你收集整理的真c++ 从二叉树到红黑树(4)之二叉平衡搜索树AVL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rosalind第28题——ros_bi
- 下一篇: 双硬盘安装双系统(win10+ubunt