学习quot;平衡二叉树quot;之摘录
附上鏈接:https://www.cnblogs.com/zhangbaochong/p/5164994.html
一、平衡二叉樹又被稱為AVL樹(有別于AVL算法),且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。這個方案很好的解決了二叉查找樹退化成鏈表的問題,把插入,查找,刪除的時間復雜度最好情況和最壞情況都維持在O(logN)。但是頻繁旋轉會使插入和刪除犧牲掉O(logN)左右的時間,不過相對二叉查找樹來說,時間上穩定了很多。
平衡二叉樹大部分操作和二叉查找樹類似,主要不同于插入刪除的時候平衡二叉樹的平衡可能被改變,并且只有從那些插入點到根節點的路徑上的節點的平衡性可能被改變,因為只有這些節點的子樹可能變化。
平衡二叉樹不平衡的情形:
把需要重新平衡的節點叫做a,由于任意兩個節點最多只有兩個兒子,因此高度不平衡時,a節點的兩棵子樹的高度相差2,容易看出,這種不平衡可能出現下面4種情況中:
? 1.對a的左兒子的左子樹進行一次插入
? 2.對a的左兒子的右子樹進行一次插入
? 3.對a的右兒子的左子樹進行一次插入
? 4.對a的右兒子的右子樹進行一次插入
? 情形1和情形4是關于a的鏡像堆成,情形2和情形3也是關于a的鏡像對稱,因此理論上看只有兩種情況,但編程的角度看還是四種情形。
? 第一種情況是插入發生在"外邊"的情形(左左或右右),該情況可以通過一次單旋轉完成調整;第二種情況是插入發生在"內部"的情形(左右或右左),這種情況比較復雜,需要通過雙旋轉來調整。
?調整措施:
?一、單旋轉
? ? ?
? ? ?上圖是左左的情況,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
#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 PreorderTravelsal(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指向最高的節點,而q指向最高節點的左子樹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) //如果左右子樹的深度大于1,而且插入的值x小于左子樹節點的值,則左左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);elset = 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 false; }//查找節點 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);elsereturn 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, 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;elsecout << "值為" << tmp << "的節點不存在" << endl;cout << "請輸入要刪除的節點:";cin >> tmp;tree.Delete(tree.root, tmp);cout << "刪除后的中序遍歷:";tree.InorderTraversal(tree.root);cout << "\n刪除后的前序遍歷:";tree.PreorderTraversal(tree.root); }附錄:這里貼出另一個鏈接(可參考著看代碼,印象更深):http://lib.csdn.net/article/datastructure/9204
總結
以上是生活随笔為你收集整理的学习quot;平衡二叉树quot;之摘录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习ModSecrity Handboo
- 下一篇: 学习算法导论-红黑树之摘录