AVL樹模擬

1.概念

雖然二叉搜索樹可以縮短查找的效率,但如果數據有序或者接近有序時二叉搜索樹樹將退化為單支樹,查找元素相當于在順序表中搜索元素,效率低下。AVL
樹是具有一下性質的二叉搜索樹:????????

????????1.它的左右子樹都是AVL樹

????????2.左右子樹的高度差的絕對值不超過1

如果一個二叉搜索樹是高度平衡的,它就是AVL樹。如果它有n個節點,其高度可保持在log N,搜索時間復雜度為O(log N);

2.節點定義

template<class T>
struct AVLTreeNode
{AVLTreeNode(T key):_bf(0), _key(key){}AVLTreeNode* _left = nullptr;AVLTreeNode* _right = nullptr;AVLTreeNode* _parent = nullptr;int _bf;					//平衡因子T _key;
};

3.AVL插入

AVL樹的插入就是在二叉搜索樹的基礎上引入了平衡因子,因此AVL樹也可以看成是二叉搜索樹。

AVL樹的插入過程可以分為兩部分:

? ? ? ? 1.按照二叉搜索樹的方式插入新節點

? ? ? ? 2.調整平衡因子

3.1 按照二叉搜索樹的方式插入新的節點

?

bool Insert(T key)
{Node* newnode = new Node(key);if (_root == nullptr){_root = newnode;return true;}Node* cur = _root;Node* parent = nullptr;//尋找插入位置while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else return false;}//插入新的節點newnode->_parent = parent;if (key < parent->_key) parent->_left = newnode;else parent->_right = newnode;
}

3.2調整平衡因子

新節點插入后,AVL樹的平衡性可能遭到破壞,因此就需要更新平衡因子,并檢測是否破壞了AVL樹的平衡性

當newnode插入后,parent的平衡因子一點需要調整,在插入之前parent的平衡因子分為三種情況:-1,0,1。

調整方式分為以下兩種:

????????當新節點插入到parent左側時,parent平衡因子-1;

????????當新節點插入到parent右側時,parent平衡因子+1;

此時parent的平衡因子有以下三種情況:0,正負1,正負2

????????1.如果此時的平衡因子為0,說明插入新的節點后parent平衡了,滿足AVL樹的性質,無需繼續向上調整。

? ? ? ? 2.如果此時平衡因子為正負1,說明插入新的節點后parent為根的樹高度增加,需要繼續向上調整。

? ? ? ? 3.如果此時平衡因子為正負2,說明parent違反了AVL樹的性質,需要對其經行旋轉處理。

cur = newnode;
while (parent)
{//更新平衡因子if (cur == parent->_left) parent->_bf--;else parent->_bf++;//檢查平衡因子狀態if (parent->_bf == 0) break;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//不平衡,旋轉else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == 2){//...}else{//...}break;}else{cout << "error: _bf";break;}
}

4. AVL樹的旋轉

?上圖在插入前,AVL樹是平衡的,新節點插入到30的左子樹(注意:此處不是左孩子)中,30左 子樹增加了一層,導致以60為根的二叉樹不平衡。

要讓60平衡,只能將60左子樹的高度減少一層,右子樹增加一層, ?即將左子樹往上提,這樣60轉下來,因為60比30大,只能將其放在30的右子樹,而如果30有右子樹,右子樹根的值一定大于30,小于60,只能將其放在60的左子樹,旋轉完成后,更新節點 的平衡因子即可。

在旋轉過程中,有以下幾種情況需要考慮: ?1. 30節點的右孩子可能存在,也可能不存在 ?2. 60可能是根節點,也可能是子樹如果是根節點,旋轉完成后,要更新根節點如果是子樹,可能是某個節點的左子樹,也可能是右子樹

a.右單旋


void _RotateR(Node* pParent)
{// pSubL: pParent的左孩子// pSubLR: pParent左孩子的右孩子,注意:該PNode* pSubL = pParent->_pLeft;PNode* pSubLR = pSubL->_pRight;// 旋轉完成之后,30的右孩子作為雙親的左孩子pParent->_pLeft = pSubLR;// 如果30的左孩子的右孩子存在,更新親雙親if (pSubLR) pSubLR->_pParent = pParent;// 60 作為 30的右孩子// 因為60可能是棵子樹,因此在更新其雙親前必須先保存60的雙親PNode pPParent = pParent->_pParent;// 更新60的雙親pParent->_pParent = pSubL;// 更新30的雙親pSubL->_pParent = pPParent;// 如果60是根節點,根新指向根節點的指針if (NULL == pPParent){_pRoot = pSubL;pSubL->_pParent = NULL;}else{// 如果60是子樹,可能是其雙親的左子樹,也可能是右子樹if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}// 根據調整后的結構更新部分節點的平衡因子pParent->_bf = pSubL->_bf = 0;
}

b.左單旋?

?

具體細節與右單旋一致。

void _RotateL(Node* pParent)
{Node* RSub = pParent->_right;Node* RSubL = RSub->_left;Node* pPParent = pParent->_parent;if (RSubL) RSubL->_parent = pParent;pParent->_right = RSubL;RSub->_left = pParent;pParent->_parent = RSub;RSub->_parent = pPParent;if (pParent == _root) _root = RSub;else{if (pPParent->_left == pParent) pPParent->_left = RSub;else pPParent->_right = RSub;}//更新平衡因子RSub->_bf = pParent->_bf = 0;
}

c.先左旋在右旋

將雙旋變成單旋后再旋轉,即:先對30進行左單旋,然后再對90進行右單旋,旋轉完成后再 考慮平衡因子的更新。

?

// 旋轉之前,60的平衡因子可能是-1/0/1,旋轉完成之后,根據情況對其他節點的平衡因子進
//行調整
void _RotateLR(PNode pParent)
{PNode pSubL = pParent->_pLeft;PNode pSubLR = pSubL->_pRight;// 旋轉之前,保存pSubLR的平衡因子,旋轉完成之后,需要根據該平衡因子來調整其他節//點的平衡因子int bf = pSubLR->_bf;// 先對30進行左單旋_RotateL(pParent->_pLeft);// 再對90進行右單旋_RotateR(pParent);if (1 == bf)pSubL->_bf = -1;else if (-1 == bf)pParent->_bf = 1;
}

d. 先右旋在左旋

void _RotateRL(Node* pParent)
{Node* RSub = pParent->_right;Node* RSubL = RSub->_left;int bf = RSubL->_bf;_RotateR(RSub);_RotateL(pParent);RSub->_bf = 0;if (bf == 1){RSub->_bf = 0;pParent->_bf = -1;}else if (bf == -1){pParent->_bf = 0;RSub->_bf = 1;}else{RSub->_bf = pParent->_bf = 0;}
}

總結:

加入以parent為根的子樹不平衡,即以parent的平衡因子為2或-2,分別考慮以下情況

? ? ? ? 1.parent的平衡因子為2,說明parent的右子樹高,設parent的右子樹的根為RSub

? ? ? ? ? ? ? ? 當RSub的平衡因子為1時,執行左單旋,

? ? ? ? ? ? ? ? 當RSub的平衡因子為-1時,執行左右雙旋。

? ? ? ? 2.parent的平衡因子為-2?,說明parent的左子樹高,設parent的左子樹的根為LSub

? ? ? ? ? ? ? ? 當LSub的平衡因子為-1時,執行右單旋。

? ? ? ? ? ? ? ? 當LSub的平衡因子為1時,執行做單旋。

當旋轉接完成后,parent為根的子樹高度已經降低,以及平衡,無需向上更新。

5.AVL樹的驗證

AVL樹實在二叉搜索樹的基礎上加了平衡性的限制,因此要驗證AVL樹可以分為兩步

? ? ? ? 1.驗證其為二叉搜索樹

? ? ? ? ? ? ? ? 如果中序遍歷結果為有序,則為二叉搜索樹

? ? ? ? 2.驗證其為平衡樹

? ? ? ? ? ? ? ? 1.每個子樹高度差的絕對值不超過1

? ? ? ? ? ? ? ? 2.節點平衡因子正確

void InOrder()
{_InOrder(_root);
}int GETHeight()
{return _GETHeight(_root);
}bool IsBalance()
{return _isBalance(_root);
}	bool _isBalance(Node* root)
{if (root->_bf >= 2 || root->_bf <= -1) return false;int HeightLeft = _GETHeight(root->_left);int HeightRight = _GETHeight(root->_right);if (abs(HeightRight - HeightLeft) > 1) return false;return _isBalance(root->_left) && _isBalance(root->_right);
}
int _GETHeight(Node* root)
{if (root == nullptr) return 0;return max(_GETHeight(root->_left), _GETHeight(root->_right)) + 1;
}
void _InOrder(Node* root)
{if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}

測試代碼?

void test_AVL01()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t1;for (auto e : a){t1.Insert(e);//cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}cout << t1.GETHeight() << endl;t1.InOrder();//cout << t1.IsBalance() << endl;
}

6.AVL樹模擬代碼

#pragma oncetemplate<class T>
struct AVLTreeNode
{AVLTreeNode(T key):_bf(0), _key(key){}AVLTreeNode* _left = nullptr;AVLTreeNode* _right = nullptr;AVLTreeNode* _parent = nullptr;int _bf;					//平衡因子T _key;
};template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:bool Insert(T key){Node* newnode = new Node(key);if (_root == nullptr){_root = newnode;return true;}Node* cur = _root;Node* parent = nullptr;//尋找插入位置while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else return false;}//插入新的節點newnode->_parent = parent;if (key < parent->_key) parent->_left = newnode;else parent->_right = newnode;//更新平衡因子//插入后AVL樹平衡,無需調整//插入后AVL樹高度增加,繼續向上調整cur = newnode;while (parent){if (cur == parent->_left) parent->_bf--;else parent->_bf++;//檢查平衡因子狀態if (parent->_bf == 0) break;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//不平衡,旋轉else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == 2){//左單旋if (parent->_right->_bf == 1){_RotateL(parent);}else{_RotateRL(parent);}}else{//右單旋if (parent->_left->_bf == -1){_RotateR(parent);}else{_RotateLR(parent);}}break;}else{cout << "error: _bf";break;}}return true;}Node* Find(const T& key){Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->_right;else if (key < cur->_key) cur = cur->_left;else return cur;}return nullptr;}size_t Size(){return _Size(_root);}void InOrder(){_InOrder(_root);}int GETHeight(){return _GETHeight(_root);}bool IsBalance(){return _isBalance(_root);}	
private:bool _isBalance(Node* root){if (root->_bf >= 2 || root->_bf <= -1) return false;int HeightLeft = _GETHeight(root->_left);int HeightRight = _GETHeight(root->_right);if (abs(HeightRight - HeightLeft) > 1) return false;return _isBalance(root->_left) && _isBalance(root->_right);}int _GETHeight(Node* root){if (root == nullptr) return 0;return max(_GETHeight(root->_left), _GETHeight(root->_right)) + 1;}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}size_t _Size(Node* root){if (root == nullptr) return 0;size_t SizeLeft = _Size(root->_left);size_t SizeRight = _Size(root->_right);return SizeLeft + SizeRight + 1;}//右旋void _RotateR(Node* pParent){Node* LSub = pParent->_left;Node* LSubR = LSub->_right;Node* pPParent = pParent->_parent;if(LSubR)LSubR->_parent = pParent;pParent->_left = LSubR;LSub->_right = pParent;pParent->_parent = LSub;LSub->_parent = pPParent;if (pParent == _root) _root = LSub;else{if (pPParent->_left == pParent) pPParent->_left = LSub;else pPParent->_right = LSub;}//更新平衡因子LSub->_bf = pParent->_bf = 0;}//左旋void _RotateL(Node* pParent){Node* RSub = pParent->_right;Node* RSubL = RSub->_left;Node* pPParent = pParent->_parent;if (RSubL) RSubL->_parent = pParent;pParent->_right = RSubL;RSub->_left = pParent;pParent->_parent = RSub;RSub->_parent = pPParent;if (pParent == _root) _root = RSub;else{if (pPParent->_left == pParent) pPParent->_left = RSub;else pPParent->_right = RSub;}//更新平衡因子RSub->_bf = pParent->_bf = 0;}void _RotateRL(Node* pParent){Node* RSub = pParent->_right;Node* RSubL = RSub->_left;int bf = RSubL->_bf;_RotateR(RSub);_RotateL(pParent);RSub->_bf = 0;if (bf == 1){RSub->_bf = 0;pParent->_bf = -1;}else if (bf == -1){pParent->_bf = 0;RSub->_bf = 1;}else{RSub->_bf = pParent->_bf = 0;}}void _RotateLR(Node* pParent){Node* LSub = pParent->_left;Node* LSubR = LSub->_right;int bf = LSubR->_bf;_RotateL(LSub);_RotateR(pParent);LSub->_bf = 0;if (bf == 0){LSubR->_bf = LSubR->_bf = 0;}else if (bf == 1){LSub->_bf = -1;pParent->_bf = 0;}else if (bf == -1){LSub->_bf = 0;pParent->_bf = 1;}}private:Node* _root = nullptr;
};void test_AVL01()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t1;for (auto e : a){int i = 1;t1.Insert(e);//cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}cout << t1.GETHeight() << endl;t1.InOrder();//cout << t1.IsBalance() << endl;
}void test_AVL02()
{const int N = 10000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();AVLTree<int> t;for (auto e : v){t.Insert(e);//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;//cout << t.IsBalance() << endl;cout << "Height:" << t.GETHeight() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 確定在的值for (auto e : v){t.Find(e);}// 隨機值/*for (size_t i = 0; i < N; i++){t.Find((rand() + i));}*/size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/37845.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37845.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/37845.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mac 如何安裝 wget

1.安裝 Homebrew2.安裝 wget3.檢測 wget 是否安裝成功 1.安裝 Homebrew 在安裝 wget 之前需要安裝一個適用于 mac 的包管理器 Homebrew&#xff0c;打開 mac 終端執行如下命令進行安裝&#xff1a; /usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/h…

【Git】GitIgnore不生效

這里可能有兩種原因&#xff0c;一個沒有刷新Git緩存&#xff0c;二是Git忽略規則有問題 更新Git緩存 git rm -r --cached . git add . git commit -m "modify git ignore rule"Ignore規則 檢查下忽略文件的目錄表示是否正確 XXX忽略任意目錄下名為XXX的文件 …

新手第一個漏洞復現:MS17-010(永恒之藍)

文章目錄 漏洞原理漏洞影響范圍復現環境復現步驟 漏洞原理 漏洞出現在Windows SMB v1中的內核態函數srv!SrvOs2FeaListToNt在處理FEA&#xff08;File Extended Attributes&#xff09;轉換時。該函數在將FEA list轉換成NTFEA&#xff08;Windows NT FEA&#xff09;list前&am…

【Golang - 90天從新手到大師】Day14 - 方法和接口

一&#xff0e; go方法 go方法&#xff1a;在函數的func和函數名間增加一個特殊的接收器類型&#xff0c;接收器可以是結構體類型或非結構體類型。接收器可以在方法內部訪問。創建一個接收器類型為Type的methodName方法。 func (t Type) methodName(parameter list) {}go引入…

在 MATLAB 中顯示 3D 圖像

文章目錄 前言1. 曲面圖 (Surface Plot)2. 網格圖 (Mesh Plot)3. 散點圖 (Scatter Plot)4. 等值線圖 (Contour Plot) 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&#xff1a; 項目需要&#xff1a; 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例…

享元模式(設計模式)

享元模式&#xff08;Flyweight Pattern&#xff09;是一種結構型設計模式&#xff0c;它通過共享細粒度對象來減少內存使用&#xff0c;從而提高性能。在享元模式中&#xff0c;多個對象可以共享相同的狀態以減少內存消耗&#xff0c;特別適合用于大量相似對象的場景。 享元模…

解決“Duplicate keys detected: ‘ ‘.This may cause an update error.”問題

問題原因 出現“Duplicate keys detected”的錯誤&#xff0c;通常表示在v-for指令中使的:key綁定值有重復。 如果前端是靜態數據&#xff0c;一般能自我避免:key綁定值有重復。如果前端是綁定的動態數據&#xff0c;那么需要另外提供一個唯一的鍵。 在這個例子中&#xff0c…

【LeetCode】接雨水

目錄 一、題目二、解法完整代碼 一、題目 給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖&#xff0c;計算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 輸入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 輸出&#xff1a;6 解釋&#xff…

面向對象,常用類,集合,異常,JDBC,mysql數據庫內容的復習,

1&#xff0c;面向對象 面向對象與面向過程對比 面向過程&#xff1a;關注過程&#xff0c;適合解決簡單直接的問題&#xff0c;代碼結構以函數為單位&#xff0c;如C語言。 面向對象&#xff1a;關注類&#xff0c;適合解決復雜問題更加適合解決復雜的項目中的問題等等&…

跨平臺編程:在Conda中搭建R語言環境的終極指南

&#x1f310; 跨平臺編程&#xff1a;在Conda中搭建R語言環境的終極指南 &#x1f310; 在數據科學和統計分析領域&#xff0c;R語言以其強大的數據處理能力和豐富的圖形表示功能而廣受歡迎。然而&#xff0c;對于習慣了使用Linux操作系統的用戶來說&#xff0c;如何方便地在…

【UML用戶指南】-23-對高級行為建模-狀態機

目錄 1、概述 2、狀態 2.1、狀態的組成 3、轉移 3.1、轉移的組成 4、高級狀態和轉移 4.1、進入效應和退出效應 4.2、內部轉移 4.3、do活動 4.4、延遲事件 4.5、子狀態機 5、子狀態 5.1、非正交子狀態 5.2、歷史狀態 5.3、正交子狀態 6、分叉與匯合 7、主動對象…

GOROOT GOPATH GOPROXY GO111MODULE

GOROOT GOROOT代表Go的安裝目錄。可執行程序go(或go.exe)和gofmt(或gofmt.exe)位于 GOROOT/bin目錄中。 配置GOROOT環境變量&#xff0c;其值為Go的安裝目錄&#xff1b;然后在環境變量PATH中添加GOROOT/bin路徑。 注意&#xff1a;GOROOT變量只是代表了安裝目錄&#xff0c;不…

泛型的實際應用示例

泛型的實際應用示例 1. 集合框架中的泛型 在Java的集合框架中&#xff0c;泛型被廣泛使用以確保類型安全并減少運行時錯誤。以下是一個使用泛型ArrayList的示例&#xff1a; java import java.util.ArrayList; import java.util.List; public class GenericCollectionsExamp…

【面試題】信息系統安全運維要做什么

信息系統安全運維是確保信息系統穩定、可靠、安全運行的一系列活動和措施。 其主要包括以下幾個方面&#xff1a; 1.系統監控&#xff1a; 實時監測信息系統的運行狀態&#xff0c;如服務器的性能指標、網絡流量、應用程序的運行情況等。通過監控工具&#xff0c;及時發現系統…

企業數據治理的下一步是數據資產管理?

隨著信息技術的飛速發展和數字化轉型的深入推進&#xff0c;企業數據已成為驅動業務增長和創新的核心要素。當企業數據治理工作取得顯著成效后&#xff0c;如何進一步發揮數據的價值&#xff0c;實現數據資產的有效管理&#xff0c;成為企業面臨的重要課題。 數據治理的基石作用…

算法練習——函數、遞歸和遞推

在此記錄一些有關函數、遞歸和遞推的問題。所有題目均來自洛谷的題單能力提升綜合題單Part1 入門階段 - 題單 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn) &#xff08;實際上都沒有用遞推做&#xff09; [NOIP2001 普及組] 數的計算 題目描述 給出正整數 n n n&#xf…

學習感悟丨在譽天學習數通HCIP怎么樣

大家好&#xff0c;我是譽天學員的徐同學&#xff0c;學習的數通HCIP課程。 在學校的時候&#xff0c;聽說下半年就要出去實習了&#xff0c;心中坎坷不安&#xff0c;現在我學到的知識遠遠不夠的。然后就想著學點東西充實一下自己的知識面和專業能力&#xff0c;有一次和同學談…

【漏洞復現】飛企互聯——SQL注入

聲明&#xff1a;本文檔或演示材料僅供教育和教學目的使用&#xff0c;任何個人或組織使用本文檔中的信息進行非法活動&#xff0c;均與本文檔的作者或發布者無關。 文章目錄 漏洞描述漏洞復現測試工具 漏洞描述 飛企互聯-FE企業運營管理平臺是一個基于云計算、智能化、大數據…

[圖解] 向量數據庫之何謂乘積量化器?

Product Quantization 在前面一節講解了向量數據庫索引相關的內容&#xff0c;那么本節將會講解其中壓縮方法的量化手段&#xff1a;乘積量化器。 簡單來說將向量的所有維度劃分為多個子空間&#xff0c;每個子空間一部分維度&#xff0c;然后每個子空間獨立去找最近距離。例如…

haproxy實現代理和負載均衡

HaProxy介紹&#xff1a; haproxy是法國開發者威利塔羅在2000年使用C語言開發的一個開源軟件&#xff0c;是一款具備高并發(一萬以上)、高性能的TCP和HTTP負載均衡器&#xff0c;支持基于cookie的持久性&#xff0c;自動故障切換&#xff0c;支持正則表達式及web狀態統計&…