您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關C語言平衡二叉樹的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
平衡二叉樹(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為根的平衡二叉樹。
同插入操作一樣,刪除結點時也有可能破壞平衡性,這就要求我們刪除的時候要進行平衡性調整。
首先在整個二叉樹中搜索要刪除的結點,如果沒搜索到直接返回不作處理,否則執行以下操作:
如果左右子樹都非空。在高度較大的子樹中實施刪除操作。
分兩種情況:
(1)、左子樹高度大于右子樹高度,將左子樹中最大的那個元素賦給當前根節點,然后刪除左子樹中元素值最大的那個節點。
(1)、左子樹高度小于右子樹高度,將右子樹中最小的那個元素賦給當前根節點,然后刪除右子樹中元素值最小的那個節點。
如果左右子樹中有一個為空,那么直接用那個非空子樹或者是NULL替換當前根節點即可。
遞歸調用,在左子樹中實施刪除。
這個是需要判斷當前根節點是否仍然滿足平衡條件,
如果滿足平衡條件,只需要更新當前根節點T的高度信息。
否則,需要進行旋轉調整:
如果T的左子節點的左子樹的高度大于T的左子節點的右子樹的高度,進行相應的單旋轉。否則進行雙旋轉。
下面給出詳細代碼實現:
AvlTree.h
#include <iostream> #include <algorithm> using namespace std; #pragma once //平衡二叉樹結點 template <typename T> struct AvlNode { T data; int height; //結點所在高度 AvlNode<T> *left; AvlNode<T> *right; AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0){} }; //AvlTree template <typename T> class AvlTree { public: AvlTree<T>(){} ~AvlTree<T>(){} AvlNode<T> *root; //插入結點 void Insert(AvlNode<T> *&t, T x); //刪除結點 bool Delete(AvlNode<T> *&t, T x); //查找是否存在給定值的結點 bool Contains(AvlNode<T> *t, const T x) const; //中序遍歷 void InorderTraversal(AvlNode<T> *t); //前序遍歷 void PreorderTraversal(AvlNode<T> *t); //最小值結點 AvlNode<T> *FindMin(AvlNode<T> *t) const; //最大值結點 AvlNode<T> *FindMax(AvlNode<T> *t) const; private: //求樹的高度 int GetHeight(AvlNode<T> *t); //單旋轉 左 AvlNode<T> *LL(AvlNode<T> *t); //單旋轉 右 AvlNode<T> *RR(AvlNode<T> *t); //雙旋轉 右左 AvlNode<T> *LR(AvlNode<T> *t); //雙旋轉 左右 AvlNode<T> *RL(AvlNode<T> *t); }; template <typename T> AvlNode<T> * AvlTree<T>::FindMax(AvlNode<T> *t) const { if (t == NULL) return NULL; if (t->right == NULL) return t; return FindMax(t->right); } template <typename T> AvlNode<T> * AvlTree<T>::FindMin(AvlNode<T> *t) const { if (t == NULL) return NULL; if (t->left == NULL) return t; return FindMin(t->left); } template <typename T> int AvlTree<T>::GetHeight(AvlNode<T> *t) { if (t == NULL) return -1; else return t->height; } //單旋轉 //左左插入導致的不平衡 template <typename T> AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t) { AvlNode<T> *q = t->left; t->left = q->right; q->right = t; t = q; t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1; return q; } //單旋轉 //右右插入導致的不平衡 template <typename T> AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t) { AvlNode<T> *q = t->right; t->right = q->left; q->left = t; t = q; t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1; return q; } //雙旋轉 //插入點位于t的左兒子的右子樹 template <typename T> AvlNode<T> * AvlTree<T>::LR(AvlNode<T> *t) { //雙旋轉可以通過兩次單旋轉實現 //對t的左結點進行RR旋轉,再對根節點進行LL旋轉 RR(t->left); return LL(t); } //雙旋轉 //插入點位于t的右兒子的左子樹 template <typename T> AvlNode<T> * AvlTree<T>::RL(AvlNode<T> *t) { LL(t->right); return RR(t); } template <typename T> void AvlTree<T>::Insert(AvlNode<T> *&t, T x) { if (t == NULL) t = new AvlNode<T>(x); else if (x < t->data) { Insert(t->left, x); //判斷平衡情況 if (GetHeight(t->left) - GetHeight(t->right) > 1) { //分兩種情況 左左或左右 if (x < t->left->data)//左左 t = LL(t); else //左右 t = LR(t); } } else if (x > t->data) { Insert(t->right, x); if (GetHeight(t->right) - GetHeight(t->left) > 1) { if (x > t->right->data) t = RR(t); else t = RL(t); } } else ;//數據重復 t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; } template <typename T> bool AvlTree<T>::Delete(AvlNode<T> *&t, T x) { //t為空 未找到要刪除的結點 if (t == NULL) return false; //找到了要刪除的結點 else if (t->data == x) { //左右子樹都非空 if (t->left != NULL && t->right != NULL) {//在高度更大的那個子樹上進行刪除操作 //左子樹高度大,刪除左子樹中值最大的結點,將其賦給根結點 if (GetHeight(t->left) > GetHeight(t->right)) { t->data = FindMax(t->left)->data; Delete(t->left, t->data); } else//右子樹高度更大,刪除右子樹中值最小的結點,將其賦給根結點 { t->data = FindMin(t->right)->data; Delete(t->right, t->data); } } else {//左右子樹有一個不為空,直接用需要刪除的結點的子結點替換即可 AvlNode<T> *old = t; t = t->left ? t->left: t->right;//t賦值為不空的子結點 delete old; } } else if (x < t->data)//要刪除的結點在左子樹上 { //遞歸刪除左子樹上的結點 Delete(t->left, x); //判斷是否仍然滿足平衡條件 if (GetHeight(t->right) - GetHeight(t->left) > 1) { if (GetHeight(t->right->left) > GetHeight(t->right->right)) { //RL雙旋轉 t = RL(t); } else {//RR單旋轉 t = RR(t); } } else//滿足平衡條件 調整高度信息 { t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; } } else//要刪除的結點在右子樹上 { //遞歸刪除右子樹結點 Delete(t->right, x); //判斷平衡情況 if (GetHeight(t->left) - GetHeight(t->right) > 1) { if (GetHeight(t->left->right) > GetHeight(t->left->left)) { //LR雙旋轉 t = LR(t); } else { //LL單旋轉 t = LL(t); } } else//滿足平衡性 調整高度 { t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1; } } return true; } //查找結點 template <typename T> bool AvlTree<T>::Contains(AvlNode<T> *t, const T x) const { if (t == NULL) return false; if (x < t->data) return Contains(t->left, x); else if (x > t->data) return Contains(t->right, x); else return true; } //中序遍歷 template <typename T> void AvlTree<T>::InorderTraversal(AvlNode<T> *t) { if (t) { InorderTraversal(t->left); cout << t->data << ' '; InorderTraversal(t->right); } } //前序遍歷 template <typename T> void AvlTree<T>::PreorderTraversal(AvlNode<T> *t) { if (t) { cout << t->data << ' '; PreorderTraversal(t->left); PreorderTraversal(t->right); } }
main.cpp
#include "AvlTree.h" int main() { AvlTree<int> tree; int value; int tmp; cout << "請輸入整數建立二叉樹(-1結束):" << endl; while (cin >> value) { if (value == -1) break; tree.Insert(tree.root,value); } cout << "中序遍歷"; tree.InorderTraversal(tree.root); cout << "\n前序遍歷:"; tree.PreorderTraversal(tree.root); cout << "\n請輸入要查找的結點:"; cin >> tmp; if (tree.Contains(tree.root, tmp)) cout << "已查找到" << endl; else cout << "值為" << tmp << "的結點不存在" << endl; cout << "請輸入要刪除的結點:"; cin >> tmp; tree.Delete(tree.root, tmp); cout << "刪除后的中序遍歷:"; tree.InorderTraversal(tree.root); cout << "\n刪除后的前序遍歷:"; tree.PreorderTraversal(tree.root); }
測試結果:
感謝各位的閱讀!關于“C語言平衡二叉樹的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。