【C++】AVL樹(旋轉、平衡因子)

🌈個人主頁:秦jh_-CSDN博客
🔥?系列專欄:https://blog.csdn.net/qinjh_/category_12575764.html?spm=1001.2014.3001.5482

?9efbcbc3d25747719da38c01b3fa9b4f.gif??

目錄

前言

AVL樹的概念

?節點

插入

AVL樹的旋轉?

新節點插入較高左子樹的左側---左左:右單旋?

新節點插入較高右子樹的右側---右右:左單旋

新節點插入較高左子樹的右側---左右:先左單旋再右單旋

?新節點插入較高右子樹的左側---右左:先右單旋再左單旋

AVL樹的驗證?

?AVL樹的性能

完整代碼


前言

????💬 hello! 各位鐵子們大家好哇。

? ? ? ? ? ? ?今日更新了AVL樹的相關內容
????🎉 歡迎大家關注🔍點贊👍收藏??留言📝

AVL樹的概念

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

解決方案:當向二叉搜索樹中插入新結點后,如果能保證每個結點的左右子樹高度之差的絕對值不超過1(需要對樹中的結點進行調整),即可降低樹的高度,從而減少平均搜索長度。

  • 它的左右子樹都是AVL樹
  • 左右子樹高度之差(簡稱平衡因子)的絕對值不超過1(-1/0/1)

如果一棵二叉搜索樹是高度平衡的,它就是AVL樹。

插入的總體原則:

  1. ?按照搜索樹規則插入
  2. 更新插入節點的祖先節點的平衡因子。
    1. 如果插入在父親左邊,父親的平衡因子--。
    2. 如果插入在父親右邊,父親的平衡因子++。
    3. 父親平衡因子==0,則父親所在子樹高度不變,不再繼續往上更新,插入結束。
    4. 父親平衡因子==1or-1,父親所在子樹高度變了,繼續往上更新。
    5. 父親平衡因子==2or-2,父親所在子樹已經不平衡了,需要旋轉處理。

?節點

插入

	bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//更新平衡因子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){//當前子樹出問題了,需要旋轉平衡一下break;}else{//理論而言不可能出現該情況assert(false);}}return true;}

上面是插入的大體流程,旋轉操作還未給出。

AVL樹的旋轉?

如果在一棵原本是平衡的AVL樹中插入一個新節點,可能造成不平衡,此時必須調整樹的結構, 使之平衡化。根據節點插入位置的不同,AVL樹的旋轉分為四種:

新節點插入較高左子樹的左側---左左:右單旋?

這里以抽象圖進行分析,因為具體的情況有很多種,無法畫出。

注意:a子樹的情況必須是插入后會引發祖先節點的更新,而不是只是內部變化。如下圖情況就不符合要求。

旋轉流程:新節點插入在a樹中,導致以60為根的二叉樹不平衡。所以就要右單旋。

右單旋:把60的左子樹高度減少,即把60取出來,讓30的右子樹變成60的左子樹,再把以60為根的樹變成30的右子樹。30成為新的根。

	void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) //節點可能為空subLR->_parent = parent;subL->_right = parent; //舊父節點變成subL的右節點Node* ppNode = parent->_parent;  //該不平衡節點可能不是根節點,所以要找到它的父節點parent->_parent = subL;				if (parent == _root)   //如果該節點是根節點{_root = subL;		_root->_parent = nullptr;}else  //不平衡節點只是一棵子樹{if (ppNode->_left == parent)  //如果舊父節點等于爺爺節點的左節點,新父節點為爺爺節點的左節點{ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;	//新父節點指向爺爺節點。}parent->_bf = subL->_bf = 0;  //只需要修改這兩個的平衡因子}

新節點插入較高右子樹的右側---右右:左單旋

參考右單旋。

左單旋和右單旋的調用如下圖:

新節點插入較高左子樹的右側---左右:先左單旋再右單旋

單旋用在一邊一直高的情況。雙旋是先一邊高再另一邊高的情況。

雙旋的的原理就是把折線變成直線,再像處理直線一樣旋轉。

雙旋可以復用單旋,但雙旋主要要搞清平衡因子的變化。

第一種情況:?

雙旋的結果:60的左邊給了30的右邊,60的右邊給了90的左邊,30和90分別成為60的左右,60成為根。

上圖是插入b引起的旋轉,當插入c時是第二種情況,如下圖:

上面兩種插入位置的不同,導致最終的平衡因子不同。

第三種情況:

h==0時,60就是新增節點,最終的平衡因子也不同。

	void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right; int bf = subLR->_bf;  //記錄未旋轉前subLR的平衡因子RotateL(parent->_left);  RotateR(parent);if (bf == -1)  //如果bf為-1,即插入在subLR的左邊{subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1) //插入在subLR的右邊{subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else{assert(false);}}

?新節點插入較高右子樹的左側---右左:先右單旋再左單旋

參考左右雙旋,注意,這里也要討論那三種情況。?

	void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);subRL->_bf = 0;if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;}else{parent->_bf = 0;subR->_bf = 0;}}

AVL樹的驗證?

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

  1. 驗證其為二叉搜索樹。如果中序遍歷可得到一個有序的序列,就說明為二叉搜索樹
  2. 驗證其為平衡樹。每個節點子樹高度差的絕對值不超過1(注意節點中如果沒有平衡因子) 節點的平衡因子是否計算正確?

?因為root是私有的,又因為需要遞歸檢查每棵子樹是否平衡,所以可以寫一個私有的_IsBalance方法,通過公有的IsBalance方法來調用。

?AVL樹的性能

AVL樹是一棵絕對平衡的二叉搜索樹,其要求每個節點的左右子樹高度差的絕對值都不超過1,這 樣可以保證查詢時高效的時間復雜度,即O(logN)。?但是如果要對AVL樹做一些結構修改的操作,性能非常低下,比如:插入時要維護其絕對平衡,旋轉的次數比較多,更差的是在刪除時, 有可能一直要讓旋轉持續到根的位置。因此:如果需要一種查詢高效且有序的數據結構,而且數據的個數為靜態的(即不會改變),可以考慮AVL樹,但一個結構經常修改,就不太適合。紅黑樹在經常進行增刪的結構中性能比AVL樹更優。

完整代碼

template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv),_bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//更新平衡因子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 && cur->_bf == -1) //左邊高,右單旋{RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1)//右邊高,左單旋{RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1){ RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent); }break;}else{//理論而言不可能出現該情況assert(false);}}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}void InOrder(){_InOrder(_root);cout << endl;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) //節點可能為空subLR->_parent = parent;subL->_right = parent; //舊父節點變成subL的右節點Node* ppNode = parent->_parent;  //該不平衡節點可能不是根節點,所以要找到它的父節點parent->_parent = subL;				if (parent == _root)   //如果該節點是根節點{_root = subL;		_root->_parent = nullptr;}else  //不平衡節點只是一棵子樹{if (ppNode->_left == parent)  //如果舊父節點等于爺爺節點的左節點,新父節點為爺爺節點的左節點{ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;	//新父節點指向爺爺節點。}parent->_bf = subL->_bf = 0;  //只需要修改這兩個的平衡因子}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}parent->_bf = subR->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);subRL->_bf = 0;if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;}else{parent->_bf = 0;subR->_bf = 0;}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right; int bf = subLR->_bf;  //記錄未旋轉前subLR的平衡因子RotateL(parent->_left);  RotateR(parent);if (bf == -1)  //如果bf為-1,即插入在subLR的左邊{subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1) //插入在subLR的右邊{subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else{assert(false);}}bool IsBalance(){return _IsBalance(_root);}int Height() //樹的高度{return _Height(_root);}int Size()  //插入的節點個數{return _Size(_root);}private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;return max(_Height(root->_left), _Height(root->_right)) + 1;}bool _IsBalance(Node* root) {if (root == nullptr) return true;int	leftHeight = _Height(root->_left);int	rightHeight = _Height(root->_right);//如果不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}//檢查平衡因子是否正確if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}private:Node* _root=nullptr; 
};void AVLTreeTest1()
{//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,int> t1;for (auto e : a){t1.Insert({e,e});cout <<"Insert:"<<e<<"->"<< t1.IsBalance() << endl;}t1.InOrder();	cout << t1.IsBalance() << endl;
}

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

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

相關文章

【C++】stack和queue的模擬實現 雙端隊列deque的介紹

&#x1f525;個人主頁&#xff1a; Forcible Bug Maker &#x1f525;專欄&#xff1a; STL || C 目錄 &#x1f308;前言&#x1f525;stack的模擬實現&#x1f525;queue的模擬實現&#x1f525;deque&#xff08;雙端隊列&#xff09;deque的缺陷 &#x1f308;為什么選擇…

基于Go 1.19的站點模板爬蟲

創建一個基于Go 1.19的站點模板爬蟲涉及到幾個關鍵步驟&#xff1a;初始化項目&#xff0c;安裝必要的包&#xff0c;編寫爬蟲邏輯&#xff0c;以及處理和存儲抓取的數據。下面是一個簡單的示例&#xff0c;使用goquery庫來解析HTML&#xff0c;并使用net/http來發起HTTP請求。…

【containerd】解決敲擊crictl images命令報錯問題

【Containerd】解決輸入crictl images命令報錯問題 文章目錄 【Containerd】解決輸入crictl images命令報錯問題問題復現解決辦法驗證結果參考鏈接 問題復現 [rootmaster01 ~]# crictl images WARN[0000] image connect using default endpoints: [unix:///var/run/dockershim…

七、Docker常規軟件安裝

目錄 一、總體步驟 二、安裝tomcat 1、docker hub上查找tomcat鏡像 三、安裝MySQL 1、查看MySQL鏡像 2、拉取MySQL鏡像到本地,本次拉取MySQL5.7 3、使用MySQL鏡像創建容器 4、使用Windows數據庫工具&#xff0c;連接MySQL實例 5、常見問題 6、創建MySQL容器實例 7、新…

DDP:微軟提出動態detection head選擇,適配計算資源有限場景 | CVPR 2022

DPP能夠對目標檢測proposal進行非統一處理&#xff0c;根據proposal選擇不同復雜度的算子&#xff0c;加速整體推理過程。從實驗結果來看&#xff0c;效果非常不錯 來源&#xff1a;曉飛的算法工程筆記 公眾號 論文: Should All Proposals be Treated Equally in Object Detect…

同聲傳譯app哪個好免費?對話交流推薦這5個

暑期到&#xff0c;也是旅游出行的好日子~自打周邊不少國家都開放免簽政策之后&#xff0c;出國游也變得更加方便了~對于外語水平不高的朋友來講&#xff0c;想要保證出行體驗&#xff0c;其實手上只要備好一個同聲傳譯app就OK&#xff01; 倘若你還不清楚都有哪些同聲傳譯app…

背部筋膜炎的癥狀及治療

背部筋膜炎&#xff0c;也稱為胸背肌筋膜炎&#xff0c;主要是由于勞損或風寒濕邪侵入引起的。其典型癥狀主要包括&#xff1a; 1、疼痛&#xff1a;背部筋膜一旦出現炎癥性病變&#xff0c;會對周圍交感神經組織產生刺激作用&#xff0c;從而引起不同程度的疼痛癥狀。 2、僵…

NAT:地址轉換技術

為什么會引入NAT&#xff1f; NAT&#xff08;網絡地址轉換&#xff09;的引入主要是為了解決兩個問題 IPv4地址短缺&#xff1a;互聯網快速發展&#xff0c;可用的公網IP地址越來越少。網絡安全&#xff1a;需要一種方法來保護內部網絡不被直接暴露在互聯網上。 IPv4 &…

低通濾波以及卡爾曼濾波

先講解幾個低通濾波&#xff0c;低通濾波比卡爾曼濾波簡單&#xff0c;因為卡爾曼濾波涉及到兩個輸入量&#xff0c;一個是控制量&#xff0c;一個是觀測量&#xff0c;而低通濾波是一個輸入量 1&#xff0c;利用工具箱配置低通濾波 參考地址&#xff1a;https://blog.csdn.net…

SystemUIService啟動-Android13

SystemUIService啟動-Android13 1、SystemUIService啟動2、其他SystemUI services啟動2.1 Dagger依賴注入2.2 Recents為例 1、SystemUIService啟動 SystemUI啟動&#xff0c;及其SystemUIService啟動 <!-- SystemUi service component --><string name"config_s…

應用層協議原理——可供應用程序使用的運輸服務

前面講過套接字是應用程序進程和運輸層協議之間的接口。在發送端的應用程序將報文推進該套接字。在該套接字的另一側&#xff0c;運輸層協議負責使該報文進入接收進程的套接字。 包括因特網在內的很多網絡提供了不止一種運輸層協議。當開發一個應用時&#xff0c;必須選擇一種可…

什么是海外倉管理自動化?策略及落地實施步驟指南

作為海外倉的管理者&#xff0c;你每天都面臨提高海外倉運營效率、降低成本和滿足客戶需求的問題。海外倉自動化管理技術為這些問題提供了不錯的解決思路&#xff0c;不過和任何新技術一樣&#xff0c;從策略到落地實施&#xff0c;都有一個對基礎邏輯的認識過程。 今天我們整…

重生奇跡mu的地圖名

地圖之一&#xff1a;勇者大陸 勇者大陸地處奇跡大陸中央。終年陰雨連綿&#xff0c;氣候潮濕悶熱。植物由充滿黑暗陰森氣氛的草地所構成。這里的NPC數量是所有地圖中最多的。因為地步交通要沖&#xff0c;所以也是玩家聚集最多的地方。 這里是劍士、魔法師、魔劍士和圣導師初…

vue3關于在線考試 實現監考功能 推流拉流

vue3 關于在線考試 實現監考功能&#xff0c; pc端考試 本質是直播推流的功能 使用騰訊云直播: 在線文檔 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><link rel"icon" href"/f…

永磁同步電機控制算法--最大轉矩電流比控制(虛擬信號注入法)

目前&#xff0c;國內外相關學者對 MTPA 控制方法進行了一系列的理論研究與仿真分析。通過研究取得的成果綜合來看&#xff0c;該控制方法主要有&#xff1a;直接公式計算法、曲線擬合法、查表法、搜索法、高頻信號注入法以及參數辨識法等。 之前的文章中已經介紹了直接公式計…

Java.Maths類的常用方法

Maths類的常用方法 Math 類是 Java 標準庫中的一個類&#xff0c;位于 java.lang 包中。它提供了一些基本的數學操作方法&#xff0c;這些方法都是靜態的。以下是 Math 類的所有方法&#xff1a; 數學常量 double E: 自然對數的底數&#xff08;約等于 2.718&#xff09;doub…

對于“百模大戰”,幾乎所有大佬的口風都180 °大轉變了?

文 | 智能相對論 作者 | 陳泊丞 在2024世界人工智能大會暨人工智能全球治理高級別會議產業發展主論壇上&#xff0c;百度創始人、董事長兼首席執行官李彥宏談了些對于AI大模型的看法&#xff0c;語驚四座。 他先是指出&#xff0c;“百模大戰造成了社會資源的巨大浪費&#x…

ubuntu 如何復制文件夾的內容

在Ubuntu中&#xff0c;您可以使用cp命令來復制文件夾的內容。如果您想要復制文件夾及其所有內容&#xff08;包括子文件夾&#xff09;&#xff0c;可以使用-r&#xff08;遞歸&#xff09;選項。 cp -r /path/to/source/folder/* /path/to/destination/folder/ 這個命令會將s…

現在2024年網絡安全真實情況還好就業嗎?_2024年網絡安全專業到底行不行了

2024年網絡安全行業的前景看起來非常樂觀。根據當前的趨勢和發展&#xff0c;一些趨勢和發展可能對2024年網絡安全行業產生影響&#xff1a; 5G技術的廣泛應用&#xff1a;5G技術的普及將會使互聯網的速度更快&#xff0c;同時也將帶來更多的網絡威脅和安全挑戰。網絡安全專家…

java-spring boot光速入門教程(超詳細!!)

目錄 一、引言 1.1 初始化配置 1.2 整合第三方框架 1.3 后期維護 1.4 部署工程 1.5 敏捷式開發 二、SpringBoot介紹 spring boot 2.1 搭建一個spring boot工程 2.2 使用idea創建項目 2.3 在線創建姿勢 2.4 項目的目錄結構 2.5 項目的運行方式 2.6 yml文件格式 2…