平衡二叉樹詳解
平衡二叉樹(Balanced Binary Tree)又被稱為AVL樹(有別於AVL演算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。這個方案很好的解決了二叉查詢樹退化成連結串列的問題,把插入,查詢,刪除的時間複雜度最好情況和最壞情況都維持在O(logN)。但是頻繁旋轉會使插入和刪除犧牲掉O(logN)左右的時間,不過相對二叉查詢樹來說,時間上穩定了很多。
平衡二叉樹大部分操作和二叉查詢樹類似,主要不同在於插入刪除的時候平衡二叉樹的平衡可能被改變,並且只有從那些插入點到根結點的路徑上的結點的平衡性可能被改變,因為只有這些結點的子樹可能變化。
平衡二叉樹不平衡的情形:
把需要重新平衡的結點叫做α,由於任意兩個結點最多隻有兩個兒子,因此高度不平衡時,α結點的兩顆子樹的高度相差2.容易看出,這種不平衡可能出現在下面4中情況中:
1.對α的左兒子的左子樹進行一次插入
2.對α的左兒子的右子樹進行一次插入
3.對α的右兒子的左子樹進行一次插入
4.對α的右兒子的右子樹進行一次插入
情形1和情形4是關於α的映象對稱,二情形2和情形3也是關於α的映象對稱,因此理論上看只有兩種情況,但程式設計的角度看還是四種情形。
第一種情況是插入發生在“外邊”的情形(左左或右右),該情況可以通過一次單旋轉完成調整;第二種情況是插入發生在“內部”的情形(左右或右左),這種情況比較複雜,需要通過雙旋轉來調整。
調整措施:
一、單旋轉
上圖是左左的情況,k2結點不滿足平衡性,它的左子樹k1比右子樹z深兩層,k1子樹中更深的是k1的左子樹x,因此屬於左左情況。
為了恢復平衡,我們把x上移一層,並把z下移一層,但此時實際已經超出了AVL樹的性質要求。為此,重新安排結點以形成一顆等價的樹。為使樹恢復平衡,我們把k2變成這棵樹的根節點,因為k2大於k1,把k2置於k1的右子樹上,而原本在k1右子樹的Y大於k1,小於k2,就把Y置於k2的左子樹上,這樣既滿足了二叉查詢樹的性質,又滿足了平衡二叉樹的性質。
這種情況稱為單旋轉。
二、雙旋轉
對於左右和右左兩種情況,單旋轉不能解決問題,要經過兩次旋轉。
對於上圖情況,為使樹恢復平衡,我們需要進行兩步,第一步,把k1作為根,進行一次右右旋轉,旋轉之後就變成了左左情況,所以第二步再進行一次左左旋轉,最後得到了一棵以k2為根的平衡二叉樹。
AVL樹的刪除操作:
同插入操作一樣,刪除結點時也有可能破壞平衡性,這就要求我們刪除的時候要進行平衡性調整。
刪除分為以下幾種情況:
首先在整個二叉樹中搜索要刪除的結點,如果沒搜尋到直接返回不作處理,否則執行以下操作:
1.要刪除的節點是當前根節點T。
如果左右子樹都非空。在高度較大的子樹中實施刪除操作。
分兩種情況:
(1)、左子樹高度大於右子樹高度,將左子樹中最大的那個元素賦給當前根節點,然後刪除左子樹中元素值最大的那個節點。
(1)、左子樹高度小於右子樹高度,將右子樹中最小的那個元素賦給當前根節點,然後刪除右子樹中元素值最小的那個節點。
如果左右子樹中有一個為空,那麼直接用那個非空子樹或者是NULL替換當前根節點即可。
2、要刪除的節點元素值小於當前根節點T值,在左子樹中進行刪除。
遞迴呼叫,在左子樹中實施刪除。
這個是需要判斷當前根節點是否仍然滿足平衡條件,
如果滿足平衡條件,只需要更新當前根節點T的高度資訊。
否則,需要進行旋轉調整:
如果T的左子節點的左子樹的高度大於T的左子節點的右子樹的高度,進行相應的單旋轉。否則進行雙旋轉。
3、要刪除的節點元素值大於當前根節點T值,在右子樹中進行刪除。
下面給出詳細程式碼實現:
AvlTree.h
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 #pragma once 5 6 //平衡二叉樹結點 7 template <typename T> 8 struct AvlNode 9 { 10 T data; 11 int height; //結點所在高度 12 AvlNode<T> *left; 13 AvlNode<T> *right; 14 AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0){} 15 }; 16 17 //AvlTree 18 template <typename T> 19 class AvlTree 20 { 21 public: 22 AvlTree<T>(){} 23 ~AvlTree<T>(){} 24 AvlNode<T> *root; 25 //插入結點 26 void Insert(AvlNode<T> *&t, T x); 27 //刪除結點 28 bool Delete(AvlNode<T> *&t, T x); 29 //查詢是否存在給定值的結點 30 bool Contains(AvlNode<T> *t, const T x) const; 31 //中序遍歷 32 void InorderTraversal(AvlNode<T> *t); 33 //前序遍歷 34 void PreorderTraversal(AvlNode<T> *t); 35 //最小值結點 36 AvlNode<T> *FindMin(AvlNode<T> *t) const; 37 //最大值結點 38 AvlNode<T> *FindMax(AvlNode<T> *t) const; 39 private: 40 //求樹的高度 41 int GetHeight(AvlNode<T> *t); 42 //單旋轉 左 43 AvlNode<T> *LL(AvlNode<T> *t); 44 //單旋轉 右 45 AvlNode<T> *RR(AvlNode<T> *t); 46 //雙旋轉 右左 47 AvlNode<T> *LR(AvlNode<T> *t); 48 //雙旋轉 左右 49 AvlNode<T> *RL(AvlNode<T> *t); 50 }; 51 52 template <typename T> 53 AvlNode<T> * AvlTree<T>::FindMax(AvlNode<T> *t) const 54 { 55 if (t == NULL) 56 return NULL; 57 if (t->right == NULL) 58 return t; 59 return FindMax(t->right); 60 } 61 62 template <typename T> 63 AvlNode<T> * AvlTree<T>::FindMin(AvlNode<T> *t) const 64 { 65 if (t == NULL) 66 return NULL; 67 if (t->left == NULL) 68 return t; 69 return FindMin(t->left); 70 } 71 72 73 template <typename T> 74 int AvlTree<T>::GetHeight(AvlNode<T> *t) 75 { 76 if (t == NULL) 77 return -1; 78 else 79 return t->height; 80 } 81 82 83 //單旋轉 84 //左左插入導致的不平衡 85 template <typename T> 86 AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t) 87 { 88 AvlNode<T> *q = t->left; 89 t->left = q->right; 90 q->right = t; 91 t = q; 92 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; 93 q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1; 94 return q; 95 } 96 97 //單旋轉 98 //右右插入導致的不平衡 99 template <typename T> 100 AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t) 101 { 102 AvlNode<T> *q = t->right; 103 t->right = q->left; 104 q->left = t; 105 t = q; 106 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; 107 q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1; 108 return q; 109 } 110 111 //雙旋轉 112 //插入點位於t的左兒子的右子樹 113 template <typename T> 114 AvlNode<T> * AvlTree<T>::LR(AvlNode<T> *t) 115 { 116 //雙旋轉可以通過兩次單旋轉實現 117 //對t的左結點進行RR旋轉,再對根節點進行LL旋轉 118 RR(t->left); 119 return LL(t); 120 } 121 122 //雙旋轉 123 //插入點位於t的右兒子的左子樹 124 template <typename T> 125 AvlNode<T> * AvlTree<T>::RL(AvlNode<T> *t) 126 { 127 LL(t->right); 128 return RR(t); 129 } 130 131 132 template <typename T> 133 void AvlTree<T>::Insert(AvlNode<T> *&t, T x) 134 { 135 if (t == NULL) 136 t = new AvlNode<T>(x); 137 else if (x < t->data) 138 { 139 Insert(t->left, x); 140 //判斷平衡情況 141 if (GetHeight(t->left) - GetHeight(t->right) > 1) 142 { 143 //分兩種情況 左左或左右 144 145 if (x < t->left->data)//左左 146 t = LL(t); 147 else //左右 148 t = LR(t); 149 } 150 } 151 else if (x > t->data) 152 { 153 Insert(t->right, x); 154 if (GetHeight(t->right) - GetHeight(t->left) > 1) 155 { 156 if (x > t->right->data) 157 t = RR(t); 158 else 159 t = RL(t); 160 } 161 } 162 else 163 ;//資料重複 164 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; 165 } 166 167 template <typename T> 168 bool AvlTree<T>::Delete(AvlNode<T> *&t, T x) 169 { 170 //t為空 未找到要刪除的結點 171 if (t == NULL) 172 return false; 173 //找到了要刪除的結點 174 else if (t->data == x) 175 { 176 //左右子樹都非空 177 if (t->left != NULL && t->right != NULL) 178 {//在高度更大的那個子樹上進行刪除操作 179 180 //左子樹高度大,刪除左子樹中值最大的結點,將其賦給根結點 181 if (GetHeight(t->left) > GetHeight(t->right)) 182 { 183 t->data = FindMax(t->left)->data; 184 Delete(t->left, t->data); 185 } 186 else//右子樹高度更大,刪除右子樹中值最小的結點,將其賦給根結點 187 { 188 t->data = FindMin(t->right)->data; 189 Delete(t->right, t->data); 190 } 191 } 192 else 193 {//左右子樹有一個不為空,直接用需要刪除的結點的子結點替換即可 194 AvlNode<T> *old = t; 195 t = t->left ? t->left: t->right;//t賦值為不空的子結點 196 delete old; 197 } 198 } 199 else if (x < t->data)//要刪除的結點在左子樹上 200 { 201 //遞迴刪除左子樹上的結點 202 Delete(t->left, x); 203 //判斷是否仍然滿足平衡條件 204 if (GetHeight(t->right) - GetHeight(t->left) > 1) 205 { 206 if (GetHeight(t->right->left) > GetHeight(t->right->right)) 207 { 208 //RL雙旋轉 209 t = RL(t); 210 } 211 else 212 {//RR單旋轉 213 t = RR(t); 214 } 215 } 216 else//滿足平衡條件 調整高度資訊 217 { 218 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; 219 } 220 } 221 else//要刪除的結點在右子樹上 222 { 223 //遞迴刪除右子樹結點 224 Delete(t->right, x); 225 //判斷平衡情況 226 if (GetHeight(t->left) - GetHeight(t->right) > 1) 227 { 228 if (GetHeight(t->left->right) > GetHeight(t->left->left)) 229 { 230 //LR雙旋轉 231 t = LR(t); 232 } 233 else 234 { 235 //LL單旋轉 236 t = LL(t); 237 } 238 } 239 else//滿足平衡性 調整高度 240 { 241 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; 242 } 243 } 244 245 return true; 246 } 247 248 //查詢結點 249 template <typename T> 250 bool AvlTree<T>::Contains(AvlNode<T> *t, const T x) const 251 { 252 if (t == NULL) 253 return false; 254 if (x < t->data) 255 return Contains(t->left, x); 256 else if (x > t->data) 257 return Contains(t->right, x); 258 else 259 return true; 260 } 261 262 //中序遍歷 263 template <typename T> 264 void AvlTree<T>::InorderTraversal(AvlNode<T> *t) 265 { 266 if (t) 267 { 268 InorderTraversal(t->left); 269 cout << t->data << ' '; 270 InorderTraversal(t->right); 271 } 272 } 273 274 //前序遍歷 275 template <typename T> 276 void AvlTree<T>::PreorderTraversal(AvlNode<T> *t) 277 { 278 if (t) 279 { 280 cout << t->data << ' '; 281 PreorderTraversal(t->left); 282 PreorderTraversal(t->right); 283 } 284 }
main.cpp
1 #include "AvlTree.h" 2 3 int main() 4 { 5 AvlTree<int> tree; 6 int value; 7 int tmp; 8 cout << "請輸入整數建立二叉樹(-1結束):" << endl; 9 while (cin >> value) 10 {相關推薦
平衡二叉樹詳解
平衡二叉樹(Balanced Binary Tree)又被稱為AVL樹(有別於AVL演算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。這個方案很好的解決了二叉查詢樹退化成連結串列的問題,把插入,查詢,刪
樹:線索二叉樹詳解
線索二叉樹介紹我們在有n個結點的二叉連結串列中,每個結點有指向左右2個孩子的指標域,所以有2n個指標域,而n個結點的二叉樹一共有n-1條分支線,也就是說,其實存在2n-(n-1) = n+1 個空指標域。空間十分浪費。在另一方面,我們對二叉樹做中序遍歷時,我只知道每個樹結點的
線索二叉樹詳解以及程式碼實現
參照《大話資料結構》188到194頁。 一、二叉樹的線索儲存結構定義 /* 二叉樹線索儲存結構定義 Link = 0,代表指向左右孩子的指標 Thread= 1 代表指向前驅或後繼的線索*/ typedef enum{ Link, Thread} Pointe
【Java面試12】常用演算法(冒泡、插入、選擇、快速)和二叉樹詳解
常用演算法(冒泡、插入、選擇、快速)和二叉樹詳解 同一問題可用不同演算法解決,而一個演算法的質量優劣將影響到演算法乃至程式的效率。演算法分析的目的在於選擇合適演算法和改進演算法。 電腦科學中,演算法的時間複雜度是一個函式,它定量描述了該演算法的執行時間。這是一個關於
二叉樹-詳解二叉排序樹
二叉搜尋樹 首先二叉排序樹也是一棵二叉樹,所謂二叉樹,就是“任何節點最多隻允許兩個子節點”,這兩個子節點稱為左右子節點。如下便是一個二叉樹。 1、二叉排序樹性質: 1、就是若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值; 2、若它的右子樹不空
根絕已知的遍歷順序(前序+中序||中序+後序)還原二叉樹詳解(轉)
最近做PAT遇到了還原二叉樹的問題,其實,這個演算法雖是基礎演算法,可是當真正比賽或者考試的時候不看模板還是不好寫的,因為遞迴的變數是要嚴格控制住的。 具體方法如下: 面試題目或多或少會出現這樣的選擇題或者簡答題: 首先我們得知道概念: 前序遍歷:先訪問當前節
紅黑二叉樹詳解及理論分析
什麼是紅-黑二叉樹? 紅-黑二叉樹首先是一顆二叉樹,它具有二叉樹的所有性質,是一種平衡二叉樹。普通二叉樹在生成過程中,容易出現不平衡的現象,即使是使用隨機演算法生成二叉樹,也是有一定概率生成不平衡的二叉樹. 如下圖所示 :
平衡二叉樹的詳解
參考慕課課程 https://www.icourse163.org/learn/ZJU-93001?tid=1003013004#/learn/content?type=detail&id=1004242207&cid=1005239477 參考資料結構與演算法分析----C
資料結構期末複習知識查漏補缺並配(帶詳解的)查漏習題(B樹,雜湊(雜湊),平衡二叉樹,KMP)
一.B樹(也叫B-)與B+樹專題 (1)B樹 重點總結: 1.結點最大的孩子數目稱為B樹的階。所以,2-3樹是3階B樹,2-3-4樹是3階B樹 2.所有葉節點位於同一層次 3. 4.,一般均是升序或降序 5.在B樹上查詢的過程是一個順指標查詢結點和在
平衡二叉樹(AVL)的插入和刪除詳解(上)
在電腦科學中,AVL樹是最先發明的自平衡二叉查詢樹。在AVL樹中任何節點的兩個子樹的高度最大差別為1,所以它也被稱為高度平衡樹。查詢、插入和刪除在平均和最壞情況下都是O(log n)。增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。AVL樹得名於它的發明者G.M.
平衡二叉樹各種演算法詳解一:紅黑樹
平衡二叉樹(Balanced Binary Tree)具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹的常用演算法有紅黑樹、AVL、Treap、伸展樹、SBT等。最小二叉平衡樹的節點的公式如下 F(n)=
平衡二叉樹旋轉詳解
平衡二叉樹的定義(AVL)定義 平衡二叉樹或者是一棵空樹,或者滿足以下的性質:它的左子樹和右子樹的高度之差的絕對值不超過1,並且左子樹和右子樹也是一個平衡二叉樹。 平衡因子 左子樹高度減去右子樹的
平衡二叉樹
因子 分享 平衡二叉樹 size 平衡因子 bsp 一點 ima 技術 對序列(49,38,65,97,76,13,27,50)構造平衡二叉樹: 步驟在圖上已經畫出來了,需要說明一點: *當插入76後,49和65的平衡因子都為-2,旋轉離76近的,即旋轉(65,97,76
java 遞歸實現平衡二叉樹
bsp get 實現 urn ole lean left current this public class 平衡二叉樹{ public class TreeNode { TreeNode left; TreeNode right;
平衡二叉樹的調整模版
tle spl class span mar eight ring null 回調 typedef struct avltreenode *avltree; typedef struct avltreenode{ int data; av
二叉排序樹和平衡二叉樹的關系
fill 樹的高度 == eight font 關系 avl樹 avi 等於 二叉排序樹: 二叉排序樹又稱二叉查找樹,亦稱二叉搜索樹。 二叉排序樹或者是一顆空樹,或者是具有下列性質的二叉樹: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根節點的值; (2)若右子
平衡二叉樹AVL樹的實現(c++STL)
pre 根節點 code 先序 blog ltr ons void 過程 #include <iostream> using namespace std; template<class Type> class AVLtree;
平衡二叉樹(AVL)與紅黑樹
數組 條件 節點 avl樹 平衡因子 src 特性 復雜度 關聯數組 一、AVL樹性質1.本身首先是一棵二叉搜索樹。2.帶有平衡條件:每個結點的左右子樹的高度之差的絕對值(平衡因子)最多為1。也就是說,AVL樹,本質上是帶了平衡功能的二叉查找樹(二叉排序樹,二叉搜索樹)。A
[leetcode]110BalancedBinaryTree平衡二叉樹
判斷 開始 help 如果 二叉 bsp body nod pos public boolean isBalanced(TreeNode root) { int res = helper(root); if (res<0) retur
LintCode 93. 平衡二叉樹
ini post 節點 str urn int 給定 nod init 題目:給定一個二叉樹,確定它是高度平衡的。對於這個問題,一棵高度平衡的二叉樹的定義是:一棵二叉樹中每個節點的兩個子樹的深度相差不會超過1。 樣例 給出二叉樹 A={3,9,20,#,#,15,7