数据结构-----AVL树的旋转操作
本文主要講解AVL的旋轉操作,供自己復習用,如有不對之處請指出。另外圖片是從鏈接處的大神那復制的,感覺文章寫的很好,可以去學習。
http://www.cnblogs.com/QG-whz/p/5167238.html具體內容可以參考鏈接處的講解。
AVL樹定義:一顆空的二叉樹是AVL樹;如果T是一棵非空的二叉樹,TL和TR分別是其左子樹和右子樹,那么當T滿足以下條件時,T是一棵AVL樹:
1.TL和TR是AVL樹;
2.|hl - hr| <= 1,其中hl和hr分別是TL和TR的高。
從定義可以知道,對于一個AVL樹來說,左右節點的高度差只能取1, 0, -1三個值。而只有我們向樹中插入一個節點或者從樹中刪除一個節點后,樹的某些節點的高度差才會改變。也就是說,在插入和刪除操作后,某些節點的左右子樹的高度差可能不再是上述三個值中的任何一個。此時就需要改變這些節點的位置,來讓其滿足AVL樹的定義。
完成一次插入或刪除操作后,根據子樹結構的不同,可以分成左旋,右旋,先左旋再右旋,先右旋再左旋四中方法來重構這個子樹。本文以插入操作為例。
1.左旋,適用場景:插入操作完成后,插入的節點是某個節點的右節點的右節點。
如圖所示,假設一段子樹結構中,根節點為4,右節點為5。此時如果我們插入6,根據二叉樹的定義,會將6插入在5的右節點。就像上圖中間那樣。我們稱插入的節點是根節點的右節點的右節點。
但是插入之后,對于根節點4來說,它的左節點的高度為0,右節點的高度為2,高度差不滿足AVL樹的定義,需要調增這些節點的位置。調整的方式就是以中間的節點為軸,向左方向旋轉。即在調整之后,5是這個子結構的根節點,4是5的左節點,6是5的右節點。而5原先的左節點變成如今4的右節點(圖中沒有展示這個)
為了實現左旋操作,我們定義一個leftRotation函數,其參數為子結構的根節點,返回值為調整后這個子結構的根節點。
好了,接下來我們可以寫出左旋函數的代碼了
template<class T> AVLTreeNode<T>* AVLTree<T>::leftRotation(AVLTreeNode<T>* pnode) {AVLTreeNode<T>* prnode = pnode->rightChild;pnode->rightChild = prnode->leftChild;prnode->leftChild = pnode;pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1;prnode->height = max(height(prnode->leftChild), height(prnode->rightChild)) + 1;return prnode; }代碼中更新了節點高度,正如剛才提到過的,節點的位置發生改變,所以需要改變節點的高度。但是要注意先更新pnode的高度,再更新prnode的高度,因為pnode是prnode的子樹。
但是為什么只需要改變節點4和節點5的高度呢?
這里說一下節點高度的概念:從該節點開始往下走,直到走到節點為NULL時停止,可以有很多條路,一條路上的節點個數為一個高度,所有的高度中最大的那個就是該節點的高度。
因為其他節點的左右子樹都沒有發生改變,所以高度不會發生變化。
用于左旋的插入是:插入的節點是根節點的右節點的右節點。
根節點的右節點的高度減去其左節點的高度為2,此時就需要左旋。調用的代碼就像下面這樣:
template<class T> AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key) {...if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(pnode->rightChild->key < key)pnode = leftRotation(pnode)...}... }因為pnode是一個引用,比如是ppnode->leftChild的引用,調用pnode = leftRotation(pnode)后,pnode改變也就導致ppnode->leftChild的改變。
2.右旋,使用場景:插入操作完成后,插入的節點是某個節點的左節點的左節點。
右旋和左旋是完全相反的兩個過程。如上圖,子樹的根節點是4,其左節點是3,插入的2是4的左節點3的左節點。此時判斷是否需要旋轉只要比較4的左節點的高度減去其右節點的高度是否為2就可以了。代碼和左旋類似:
template<class T> AVLTreeNode<T>* AVLTree<T>::rightRotation(AVLTreeNode<T>* pnode) {AVLTreeNode<T>* plnode = pnode->leftChild;pnode->leftChild = plnode->rightChild;plnode->rightChild = pnode;pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1 ;plnode->height = max(height(plnode->leftChild), height(plnode->rightChild)) + 1;return plnode; }同樣也需要更新節點的高度,但是更新高度應該先更新pnode,再更新plnode,因為此時plnode才是這個子樹的根節點,它的高度需要借助其左右節點來計算,會用到pnode的高度。
使用代碼的情況如左旋一樣:
template<class T> AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key) {...if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(pnode->leftChild->key > key)pnode = rightRotation(pnode)...}... }3.先左旋后右旋,使用場景:插入操作完成后,插入的節點是某個節點的左節點的右節點。
這種情況,應該先對以0為根節點的子結構進行左旋操作,效果如上圖第二行中間,然后再去以2為根節點的子結構進行右旋操作。
因為已經定義了左旋和右旋操作,所以左右旋和右左旋都比較好實現:
template<class T> AVLTreeNode<T>* AVLTree<T>::leftRightRotation(AVLTreeNode<T>* pnode) {pnode->leftChild = leftRotation(pnode->leftChild);return rightRotation(pnode); }調用的情況和右旋對應: template<class T> AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key) {...if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(pnode->leftChild->key > key)pnode = rightRotation(pnode)else if(pnode->leftChild->key < key)pnode = leftRightRotation(pnode);...}... }4.先右旋后左旋,使用場景:插入操作完成后,插入的節點是某個節點的右節點的左節點。
這種情況,應該先對以8為根節點的子結構進行右旋操作,效果如第二行中間的圖所示,再對以6為根節點的子結構進行左旋操作。
代碼如下:
template<class T> AVLTreeNode<T>* AVLTree<T>::rightLeftRotation(AVLTreeNode<T>* pnode) {pnode->rightChild = rightRotation(pnode->rightChild);return leftRotation(pnode); }調用的方式和左旋對應: template<class T> AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key) {...if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(pnode->rightChild->key < key)pnode = rightRotation(pnode)else if(pnode->rightChild->key > key)pnode = rightLeftRotation(pnode);...}... }
總結
以上是生活随笔為你收集整理的数据结构-----AVL树的旋转操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++学习笔记-----在一个构造函数中
- 下一篇: 数据结构-----AVL树的插入删除操作