日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅析AVL树

發(fā)布時間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅析AVL树 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1.為什么提出AVL樹

學(xué)習(xí)完搜索二叉樹以后,我們應(yīng)該想到一個問題,如果我們的搜索二叉樹的趨向于單鏈的形式,類似于:

2.二叉平衡樹概念和結(jié)構(gòu)

?

為了解決上述問題,所以提出了一個概念,叫做二叉平衡樹。

二叉平衡樹,相對于二叉搜索樹,引入了一個叫做平衡因子的概念。
平衡因子:平衡因子就是右子樹的深度-左子樹的深度。

二叉平衡樹的規(guī)則是:每一個節(jié)點的平衡因子的絕對值都要小于2。

所以我們需要給一個平衡因子。

二叉平衡樹的結(jié)構(gòu):

template<typename K,typename V> struct AVLBinaryTreeNode {typedef AVLBinaryTreeNode<K, V> Node;AVLBinaryTreeNode(const K& key,const V& value):_left(NULL),_right(NULL), _parent(NULL),_key(key), _bf(0), _value(value){}Node* _left;Node* _right;Node* _parent;int _bf; //來保存右子樹高度-左子樹高度,平衡因子。K _key;V _value; };


3.二叉平衡樹的平衡化旋轉(zhuǎn)

對于二叉平衡樹來說,最重要的就是它的旋轉(zhuǎn)算法,因為他要保證所有節(jié)點的平衡因子保持在0,1,-1,所以當平衡因子為2或者-2的時候,我們需要調(diào)整,這個時候就引入了旋轉(zhuǎn)這個概念。

例如:

比如上面的這個例子,我們可以看出來在這我們的3已經(jīng)不滿足二叉平衡樹的條件了,所以在這里我們應(yīng)該進行旋轉(zhuǎn)。

另外在這里需要知道,對于葉子節(jié)點,它的平衡因子都是0。

單旋轉(zhuǎn)

首先我們來看單旋轉(zhuǎn),單旋轉(zhuǎn)分為兩種情況:左單旋和右單旋

示例代碼:

//左旋void _RotateL(Node *parent){assert(parent);Node* SubR = parent->_right;Node* SubRL = SubR->_left;parent->_right = SubRL;if (SubRL)SubRL->_parent = parent;Node * ppNode = parent->_parent; //記錄根的父親 SubR->_left = parent; parent->_parent = SubR;if (ppNode==NULL) //考慮根節(jié)點的情況{_root = SubR;SubR->_parent = NULL;}else{if (ppNode->_left == parent) //判斷旋轉(zhuǎn)以后的根應(yīng)該鏈接在根的父親的那邊{ppNode->_left = SubR;}else{ppNode->_right = SubR;}SubR->_parent = ppNode;}parent->_bf = SubR->_bf = 0; //重置平衡因子} //右旋void _RotateR(Node *parent){Node *SubL = parent->_left;Node *SubLR = SubL->_right;parent->_left = SubLR;if (SubLR) {SubLR->_parent = parent;}Node* ppNode = parent->_parent; //記錄根節(jié)點父親SubL->_right = parent;parent->_parent = SubL;if (ppNode == NULL) //考慮根節(jié)點情況{_root = SubL;SubL->_parent = NULL;}else{if (ppNode->_left == parent) //判斷旋轉(zhuǎn)以后的根應(yīng)該鏈接在根的父親的那邊{ppNode->_left = SubL;}else{ppNode->_right = SubL;}SubL->_parent = ppNode;}parent->_bf = SubL->_bf = 0; //重置平衡因子}

雙旋轉(zhuǎn)

平衡二叉樹有時的旋轉(zhuǎn)是雙旋轉(zhuǎn),雙旋轉(zhuǎn)有一個特點,就是他的parent節(jié)點和下一個節(jié)點平衡因子需要異號。

在這,根據(jù)sublr的平衡因子不同,分為了3種情況進行插入。接下來,我們對每一種情況進行分析,講解。

第一種情況:
是sublr的平衡因子為0,這個時候就可以把sublr當作一個要插入的葉子節(jié)點來理解,這樣subl的平衡因子為1,parentde平衡因子為-2,這樣,就構(gòu)成了雙旋轉(zhuǎn)。

第二種情況:
sublr的平衡因子為-1,這個時候就是在sublr的左子樹進行插入節(jié)點操作,這樣,subl的平衡因子變?yōu)?,parent的平衡因子變?yōu)?2。

?

第三種情況:
第三種情況所說的就是sublr的平衡因子為1,這個時候插入點是sublr的右樹。這樣,subl的平衡因子變?yōu)?,parent的平衡因子變?yōu)?2。

上述的就是進行右左雙旋時的三種情況,我們可以畫一個表格總結(jié)下:

示例代碼:

//左右雙旋void _RotateLR(Node *parent){//進行雙旋轉(zhuǎn)的時候通過在這里的旋轉(zhuǎn)以后的根節(jié)點的bf進行判斷Node* SubL = parent->_left;Node* SubLR = SubL->_right;//計算旋轉(zhuǎn)以后的根節(jié)點的bfint bf = SubLR->_bf;_RotateL(SubL);_RotateR(parent);//如果bf為0,代表插入點就是這個節(jié)點,這個時候所有旋轉(zhuǎn)后的bf皆為0if (bf == 0){SubL->_bf = parent->_bf = 0;}//當bf為1時,這個時候相當于是在給bf的右樹插入,所以插入以后subL旋轉(zhuǎn)后的右邊高度為h,else if (bf == 1){SubL->_bf = -1;parent->_bf = 0;}//當bf為-1,這時相當于是給bf的左樹進行插入else{SubL->_bf = 0;parent->_bf = 1;}SubLR->_bf = 0;}

右左雙旋轉(zhuǎn)

接下來我們進行另外一種雙旋轉(zhuǎn)的分析,右左雙旋,有了上面分析的基礎(chǔ),我相信,下面的分析,你一看就能理解,通過單旋轉(zhuǎn),我們就可以看到,旋轉(zhuǎn)是鏡像的

?

- subrl的bf==0

- subrl的bf==1

  • subrl的bf==-1

由于右左旋轉(zhuǎn)和上述的左右旋轉(zhuǎn)分析的思路是一樣的,所以我也不就在太詳細的介紹了。

?

void _RotateRL(Node *parent){//雙旋轉(zhuǎn)根據(jù)插入不同進行調(diào)整pf,Node* SubR = parent->_right;Node* SubRL = SubR->_left;//根據(jù)最后的根節(jié)點的bf進行調(diào)整int bf = SubRL->_bf;_RotateR(SubR);_RotateL(parent);//當bf為0,代表插入的就是他這個節(jié)點,所有的bf都變作0if (bf == 0){parent->_bf = SubR->_bf = 0;}//當bf為-1,代表這時是在這個根的左樹插入,h-1高度變作h。else if (bf == -1){parent->_bf = 0;SubR->_bf = 1;}//當bf為1時,代表是在這個旋轉(zhuǎn)后的根的右樹插入,h-1的高度作h。else{parent->_bf = -1;SubR->_bf = 0;}SubRL->_bf = 0;}

4.二叉平衡樹的插入和刪除

平衡二叉樹的插入算法,和搜索二叉樹的插入算法類似,依然是查找到為空的地方,然后進行創(chuàng)建新節(jié)點,進行插入,不過這里多了一個parent指針,所以要記得維護,又因為平衡二叉樹的特性,所以不能忘了要從插入節(jié)點向上檢查,確保向上始終為平衡二叉樹,否則,需要進行平衡化旋轉(zhuǎn)。

//插入算法 bool Insert(const K& key,const V& value){if (_root == NULL){_root = new Node(key, value);return true;}Node* parent = NULL;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key > key){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}while (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){return true;}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 (cur->_bf == 1){_RotateL(parent);return true;}else{_RotateRL(parent);//parent->_bf = _Height(parent->_right) - _Height(parent->_left);//_RotateRL(parent);return true;}}if (parent->_bf == -2){if (cur->_bf == 1){_RotateLR(parent);//旋轉(zhuǎn)完以后必須進行重新對parent配置_bf//parent->_bf = _Height(parent->_right) - _Height(parent->_left);return true;}else{_RotateR(parent);return true;}}}}return true;}

至于刪除算法,因為刪除的很復(fù)雜,在書上看到的是如果在你刪除的節(jié)點數(shù)少與一半的時候,這個時候推薦使用懶惰刪除,

規(guī)則就是:簡單的標記要刪除的節(jié)點是為已刪除。然后在查找等例程里面都人為的忽視標記過的節(jié)點。如果標記刪除的節(jié)點大于總數(shù)一半,就進行一次遍歷,刪除所有標記節(jié)點。

5.二叉平衡樹的查找

AVL樹的

bool Find(const K& key){if (_root == NULL)return false;Node *cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key>cur->_key){cur = cur->_right;}else{return true;}}return false;}

查找和搜索二叉樹的類似,在這里我就不多說了,直接曬代碼。因為在你插入中已經(jīng)有這方面的邏輯了。

6.總結(jié)

AVL樹是為了解決二叉搜素樹中類似鏈式的樹的時間復(fù)雜度太高的問題,如果采用了AVL樹,大致時間復(fù)雜度都可以變?yōu)镺(logN),這樣就更加高效了。AVL樹種最關(guān)鍵的就是它的旋轉(zhuǎn),分為單旋轉(zhuǎn)和多旋轉(zhuǎn)。AVL樹是后續(xù)學(xué)習(xí)紅黑樹的基礎(chǔ)。

#pragma once#include<iostream> #include<cstdlib> #include<cassert>using namespace std;template<typename K,typename V> struct AVLBinaryTreeNode {typedef AVLBinaryTreeNode<K, V> Node;AVLBinaryTreeNode(const K& key,const V& value):_left(NULL),_right(NULL), _parent(NULL),_key(key), _bf(0), _value(value){}Node* _left;Node* _right;Node* _parent;int _bf; //來保存左右子樹的高度差,平衡因子。K _key;V _value; };template<typename K, typename V> class AVLBinaryTree { public:typedef AVLBinaryTreeNode<K, V> Node; public://構(gòu)造函數(shù)AVLBinaryTree():_root(NULL){}//析構(gòu)函數(shù)~AVLBinaryTree(){_DestoryTree(_root);}//拷貝構(gòu)造AVLBinaryTree(const AVLBinaryTree<K, V> & a){_root = _Copy(_root);}//現(xiàn)代寫法賦值A(chǔ)VLBinaryTree<K,V>operator =(const AVLBinaryTree<K,V>& d){if (this != &d){AVLBinaryTree<K,V> tmp(d);std::swap(tmp._root,_root);}return *this;}//插入節(jié)點bool Insert(const K& key,const V& value){if (_root == NULL){_root = new Node(key, value);return true;}Node* parent = NULL;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key > key){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}while (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){return true;}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 (cur->_bf == 1){_RotateL(parent);return true;}else{_RotateRL(parent);//parent->_bf = _Height(parent->_right) - _Height(parent->_left);//_RotateRL(parent);return true;}}if (parent->_bf == -2){if (cur->_bf == 1){_RotateLR(parent);//旋轉(zhuǎn)完以后必須進行重新對parent配置_bf//parent->_bf = _Height(parent->_right) - _Height(parent->_left);return true;}else{_RotateR(parent);return true;}}}}return true;}//查找節(jié)點bool Find(const K& key){if (_root == NULL)return false;Node *cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key>cur->_key){cur = cur->_right;}else{return true;}}return false;}//判斷是否為AVL樹//bool IsAVLBinaryTree()//{// return _IsAVLBinaryTree(_root);//}bool IsAVLBinaryTree(){int depth = 0;return _IsAVLBinaryTree(_root, depth);}//中序遍歷void Inorderprint(){_Inorderprint(_root); cout << endl;}//得到高度size_t Height() const{_Height(_root);} protected://拷貝構(gòu)造二叉樹Node* _Copy(Node *root){Node * NewNode = NULL;if (root != NULL){NewNode = new Node(root->_value);NewNode->_left = _Copy(root->_left);NewNode->_right = _Copy(root->_right);}return NewNode;}//樹的高度size_t _Height(Node *root){if (root == NULL)return 0;size_t lheight = _Height(root->_left) + 1;size_t rheight = _Height(root->_right) + 1;return lheight > rheight ? lheight : rheight;}//判斷二叉樹是否為平衡二叉樹/*bool _IsAVLBinaryTree(Node *root){if (root==NULL){return true;}int heightorder = abs((int)(_Height(root->_right) - _Height(root->_left)));return ((heightorder<2)&&(_IsAVLBinaryTree(root->_left))&& (_IsAVLBinaryTree(root->_right)));}*///判斷二叉樹是否為平衡二叉樹,優(yōu)化版本bool _IsAVLBinaryTree(Node *root, int & depth){//如果為空,往父節(jié)點返if (root == NULL){depth = 0;return true;}//記錄左節(jié)點和右節(jié)點深度int left = 0;int right = 0;//采取傳引用的方式,下層遞歸進行對深度修改以后會影響上一層if (_IsAVLBinaryTree(root->_left, left) && _IsAVLBinaryTree(root->_right, right)){//計算平衡因子int pf = right - left;//判斷平衡因子是否合法if (pfIsInvaild(pf)){//合法就讓自身加上子樹的深度,然后因為是傳引用,所以當遞歸回到上一層的時候,上層中的right和left就是左右子樹的深度depth = 1 + (right > left ? right : left);return true;}}return false;}//判斷平衡因子是否合法bool pfIsInvaild(const int& pf){return abs(pf) < 2;}//中序遞歸遍歷void _Inorderprint(Node *root){if (root == NULL){return;}_Inorderprint(root->_left);cout << root->_key<<" ";cout << root->_bf << endl;_Inorderprint(root->_right);}//銷毀AVL樹Node* _DestoryTree(Node*& root){if (root != NULL){root->_left = _DestoryTree(root->_left);root->_right = _DestoryTree(root->_right);delete root;root = NULL;}return root;}//左旋void _RotateL(Node *parent){assert(parent);Node* SubR = parent->_right;Node* SubRL = SubR->_left;parent->_right = SubRL;if (SubRL)SubRL->_parent = parent;Node * ppNode = parent->_parent; //記錄根的父親 SubR->_left = parent; parent->_parent = SubR;if (ppNode==NULL) //考慮根節(jié)點的情況{_root = SubR;SubR->_parent = NULL;}else{if (ppNode->_left == parent) //判斷旋轉(zhuǎn)以后的根應(yīng)該鏈接在根的父親的那邊{ppNode->_left = SubR;}else{ppNode->_right = SubR;}SubR->_parent = ppNode;}parent->_bf = SubR->_bf = 0; //重置平衡因子}//先左旋后右旋//void _RotateLR(Node *parent)//{// Node* SubL = parent->_left;// Node* SubLR = SubL->_right;// SubL->_right = SubLR->_left;// if (SubLR->_left)// SubL->_right->_parent = SubL;// SubLR->_left = SubL;// SubL->_parent = SubLR;// parent->_left = SubLR;// SubLR->_parent = parent;// //判斷SubLR的左子樹是否存在,如果存在,在調(diào)整過程當中,需要進行bf的調(diào)整// //SubLR的left最后是變成了SubL的left,所以當pf為1時,這個時候調(diào)整過去,SubL的pf就是左樹比右樹大。// if (SubLR->_bf <= 0)// SubL->_bf = 0;// else// SubL->_bf = -1;// parent->_left = SubLR->_right;// if (SubLR->_right)// {// parent->_left->_parent = parent;// }// Node *ppnode = parent->_parent;// SubLR->_right = parent;// parent->_parent = SubLR;// if (ppnode == NULL)// {// _root = SubLR;// SubLR->_parent = NULL;// }// else// {// if (parent == ppnode->_left)// {// ppnode->_left = SubLR;// }// else// {// ppnode->_right = SubLR;// }// SubLR->_parent = ppnode;// }// //同上,這是看SubLR的right是否存在。// if (SubLR->_bf == -1)// parent->_bf = 1;// else// parent->_bf = 0;// SubLR->_bf = 0;//}void _RotateLR(Node *parent){//進行雙旋轉(zhuǎn)的時候通過在這里的旋轉(zhuǎn)以后的根節(jié)點的bf進行判斷Node* SubL = parent->_left;Node* SubLR = SubL->_right;//計算旋轉(zhuǎn)以后的根節(jié)點的bfint bf = SubLR->_bf;_RotateL(SubL);_RotateR(parent);//如果bf為0,代表插入點就是這個節(jié)點,這個時候所有旋轉(zhuǎn)后的bf皆為0if (bf == 0){SubL->_bf = parent->_bf = 0;}//當bf為1時,這個時候相當于是在給bf的右樹插入,所以插入以后subL旋轉(zhuǎn)后的右邊高度為h,else if (bf == 1){SubL->_bf = -1;parent->_bf = 0;}//當bf為-1,這時相當于是給bf的左樹進行插入else{SubL->_bf = 0;parent->_bf = 1;}SubLR->_bf = 0;}//先右旋后左旋//void _RotateRL(Node *parent)//{// Node *SubR = parent->_right;// Node *SubRL = SubR->_left;// //右旋// SubR->_left = SubRL->_right;// if (SubRL->_right)// SubR->_right->_parent = SubR;// parent->_right = SubRL;// SubRL->_parent = parent;// SubRL->_right = SubR;// SubR->_parent = SubRL;// if (SubRL->_bf >= 0)// SubR->_bf = 0;// else// SubR->_bf = 1;// //左旋// parent->_right = SubRL->_left;// if (SubRL->_left)// parent->_right->_parent = parent;// Node *ppNode = parent->_parent;// SubRL->_left = parent;// parent->_parent = SubRL;// if (ppNode == NULL)// {// _root = SubRL;// SubRL->_parent = NULL;// }// else// {// if (ppNode->_left == parent)// {// ppNode->_left = SubRL;// }// else// {// ppNode->_right = SubRL;// }// SubRL->_parent = ppNode;// }// if (SubRL->_bf == 1)// parent->_bf = -1;// else// parent->_bf = 0;//}//右旋void _RotateRL(Node *parent){//雙旋轉(zhuǎn)根據(jù)插入不同進行調(diào)整pf,Node* SubR = parent->_right;Node* SubRL = SubR->_left;//根據(jù)最后的根節(jié)點的bf進行調(diào)整int bf = SubRL->_bf;_RotateR(SubR);_RotateL(parent);//當bf為0,代表插入的就是他這個節(jié)點,所有的bf都變作0if (bf == 0){parent->_bf = SubR->_bf = 0;}//當bf為-1,代表這時是在這個根的左樹插入,h-1高度變作h。else if (bf == -1){parent->_bf = 0;SubR->_bf = 1;}//當bf為1時,代表是在這個旋轉(zhuǎn)后的根的右樹插入,h-1的高度作h。else{parent->_bf = -1;SubR->_bf = 0;}SubRL->_bf = 0;}//右旋void _RotateR(Node *parent){Node *SubL = parent->_left;Node *SubLR = SubL->_right;parent->_left = SubLR;if (SubLR) {SubLR->_parent = parent;}Node* ppNode = parent->_parent; //記錄根節(jié)點父親SubL->_right = parent;parent->_parent = SubL;if (ppNode == NULL) //考慮根節(jié)點情況{_root = SubL;SubL->_parent = NULL;}else{if (ppNode->_left == parent) //判斷旋轉(zhuǎn)以后的根應(yīng)該鏈接在根的父親的那邊{ppNode->_left = SubL;}else{ppNode->_right = SubL;}SubL->_parent = ppNode;}parent->_bf = SubL->_bf = 0; //重置平衡因子} protected:Node* _root; };

?

總結(jié)

以上是生活随笔為你收集整理的浅析AVL树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。