歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

AVL樹 算法思想與代碼實現

AVL樹是高度平衡的二叉搜索樹,按照二叉搜索樹(Binary Search Tree)的性質,AVL首先要滿足:

若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉搜索樹。

AVL樹的性質:

  1. 左子樹和右子樹的高度之差的絕對值不超過1
  2. 樹中的每個左子樹和右子樹都是AVL樹
  3. 每個節點都有一個平衡因子(balance factor--bf),任一節點的平衡因子是-1,0,1之一

(每個節點的平衡因子bf 等於右子樹的高度減去左子樹的高度 )    

構建AVL樹節點

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ////    AVL樹的節點類 template<class K,class V> class AVLTreeNode {     K _key;          V _value;     int  _bf;//平衡因子 -1,0,1(每個節點的平衡因子等於右子樹的高度減去左子樹的高度)     AVLTreeNode<K, V>* _parent;  //指向父節點的指針     AVLTreeNode<K, V>* _left;        //指向左孩子的指針     AVLTreeNode<K, V>* _right;        //指向右孩子的指針       AVLTreeNode(const K& key = K(), const V& value = V())         :_key(key)         , _value(value)         , _bf(0)         , _parent(NULL)         , _left(NULL)         , _right(NULL)     {} };

 


插入數據:

插入數據以後,父節點的平衡因子必然會被改變!

首先判斷父節點的平衡因子是否滿足性質1(-1<= parent->_bf <=1),如果滿足,則要回溯向上檢查插入該節點是否影響了其它節點的平衡因子值!

  • 當父節點的平衡因子等於0時,父節點所在的子樹已經平衡,不會影響其他節點的平衡因子了。
  • 當父節點的平衡因子等於1或者-1時,需要繼續向上回溯一層,檢驗祖父節點的平衡因子是否滿足條件(把父節點給當前節點)。
  • 當父節點的平衡因子等於2或者-2時,不滿足性質1,這時需要進行旋轉 來降低高度 :   

旋轉的目的是為了降低高度  

 旋轉的一般形態:

旋轉至少涉及三層節點,所以至少要向上回溯一層 ,才會發現非法的平衡因子並進行旋轉

向上回溯校驗時,需要進行旋轉的幾種情況:

1. 當前節點的父節點的平衡因子等於2時,說明父節點的右樹比左樹高:

  • 這時如果當前節點的平衡因子等於1,那麼當前節點的右樹比左樹高,形如“ \ ”,需要進行左旋;
  • 如果當前節點的平衡因子等於-1,那麼當前節點的右樹比左樹低,形如“ > ”,需要進行右左雙旋!

2. 當前節點的父節點的平衡因子等於-2時,說明父節點的右樹比左樹低:

  • 這時如果當前節點的平衡因子等於-1,那麼當前節點的右樹比左樹低,形如“ / ”,需要進行右旋;
  • 如果當前節點的平衡因子等於1,那麼當前節點的右樹比左樹高,形如“ < ”,需要進行左右雙旋!  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 //  AVLTree插入算法 template<class K, class V> bool AVLTree<K,V>::Insert(const K& key, const V& value) {     //1.空樹     if (_root == NULL)     {         _root = new AVLTreeNode<K, V>(key, value);         return true;     }           //2.AVL樹不為NULL     AVLTreeNode<K, V>* parent = NULL;     AVLTreeNode<K, V>* 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 AVLTreeNode<K, V>(key, value);         cur->_parent = parent;         if (parent->_key > key)             parent->_left = cur;         else             parent->_right = cur;           while (parent)         {             //更新平衡因子             if (cur == parent->_left)                 parent->_bf--;             else if (cur == parent->_right)                 parent->_bf++;               //檢驗平衡因子是否合法             if (parent->_bf == 0)                 break;             else if (parent->_bf == -1 || parent->_bf == 1)             // 回溯上升 更新祖父節點的平衡因子並檢驗合法性                 cur = parent;                 parent = cur->_parent;             }             else  //  2 -2 平衡因子不合法 需要進行旋轉 降低高度             {                 if (parent->_bf == 2)                 {                     if (cur->_bf == 1)                         _RotateL(parent);                     else                         _RotateRL(parent);                 }                 else if (parent->_bf == -2)                 {                     if (cur->_bf == -1)                         _RotateR(parent);                     else                         _RotateLR(parent);                 }                 break;             }         } }

   


左旋的兩種情況:

1.parent有兩個孩子:沒有插入節點c之前處於平衡狀態,插入c之後,平衡被破壞,向上回溯檢驗祖父節點的平衡因子,當其bf=2 時,以此節點為軸進行左旋

2.parent有一個孩子:沒有插入節點a之前處於平衡狀態,插入節點a之後,parent節點的平衡因子bf=2不滿足AVL樹的性質,要以parent為軸進行左旋

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 //左旋 template<class K, class V> void AVLTree<K, V>::_RotateL(AVLTreeNode<K, V>*&  parent) {     AVLTreeNode<K, V>* subR = parent->_right;     AVLTreeNode<K, V>* subRL = subR->_left;     AVLTreeNode<K, V>* ppNode = parent->_parent;      //標記祖先節點       //1.構建parent子樹 鏈接parent和subRL     parent->_right = subRL;     if (subRL) subRL->_parent = parent;     //2.構建subR子樹 鏈接parent和subR     subR->_left = parent;     parent->_parent = subR;     //3.鏈接祖先節點和subR節點     subR->_parent = ppNode;     if (ppNode== NULL)     {//如果祖先節點為NULL,說明目前的根節點為subR         _root = subR;     }     else     //將祖先節點和subR節點鏈接起來         if (parent == ppNode->_left)             ppNode->_left = subR;         else             ppNode->_right = subR;     }     //4.重置平衡因子     parent->_bf = 0;     subR->_bf = 0;     //5.更新subR為當前父節點     parent = subR; }

右旋的兩種情況:

1. parent既有左孩子又有右孩子:插入c之前處於平衡態,插入c之後parent的平衡因子變為-2,這時要以parent為軸進行旋轉

 

2. parent只有一個孩子:插入a之前處於平衡狀態,插入之後subL與parent的平衡因子被改變,需要以parent為軸進行旋轉

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ///右旋 template<class K, class V> void AVLTree<K, V>::_RotateR(AVLTreeNode<K, V>*&  parent) {     AVLTreeNode<K, V>* subL = parent->_left;     AVLTreeNode<K, V>* subLR = subL->_right;     AVLTreeNode<K, V>* ppNode = parent->_parent;      //標記祖先節點     //1.構建parent子樹 將parent和subLR鏈接起來     parent->_left = subLR;     if (subLR) subLR->_parent = parent;     //2.構建subL子樹 將subL與parent鏈接起來     subL->_right = parent;     parent->_parent = subL;     //3.將祖先節點與sunL鏈接起來     if (ppNode == NULL)     //如果祖先為NULL,說明當前subL節點為根節點         subL->_parent = NULL;         _root = subL;     }     else     {         subL->_parent = ppNode;         if (ppNode->_left == parent)             ppNode->_left = subL;         else if (ppNode->_right == parent)             ppNode->_right = subL;     }     //4.重置平衡因子     parent->_bf = 0;     subL->_bf = 0;     //5.更新subL為當前父節點     parent = subL; }

 左右雙旋:

1. parent只有一個孩子:在插入節點sunLR之前,AVL樹處於平衡狀態,左右子樹高度差的絕對值不超過1。

  由於插入了節點subLR導致grandfather的平衡因子變為-2,平衡樹失衡,所以需要利用旋轉來降低高度!

  • 首先以subL為軸,將subLR向上提(左旋),將grandfather、parent和subL旋轉至一條直線上;
  • 再以parent為軸將之前的subLR向上提(右旋),左樹的高度降1,grandfather的平衡因子加1後變為-1,恢復平衡狀態。
  • 雙旋完成後將parent、subL的平衡因子置為0即可,左右雙旋也就完成啦!

2. parent有兩個孩子:沒有插入subRL或subRR之前的AVL樹一定是處於平衡狀態的,並且滿足AVL樹的性質。

  正是由於插入了節點subRL或者subRR,導致其祖先節點的平衡因子被改變,grandfather的平衡因子變為-2,平衡態比打破,需要進行旋轉來降低高度!

  • 首先parent為軸將subR節點往上提至原parent的位置(左旋),將grandfather、parent 和 subR旋至一條直線上;
  • 再以grandfather為軸將subR往上提至grandfather的位置(右旋),此時以subR為根的左右子樹的高度相同,恢復了平衡態!

parent有兩個孩子時,要看插入的節點是subR的右孩子還是左孩子,雙旋後對平衡因子的修改分兩種情況:

  • subR的平衡因子為1,即subR有右孩子無左孩子(有subRR但無subRL),雙旋之後將grandfather的平衡因子置為0,將parent的平衡因子置為-1;
  • subR的平衡因子為-1,即subR有左孩子無右孩子(有subRL但無subRR),雙旋之後將grandfather的平衡因子置為1,將parent的平衡因子置為0;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 //左右雙旋 template<class K, class V> void AVLTree<K, V>::_RotateLR(AVLTreeNode<K, V>*&  parent) {     AVLTreeNode<K, V>* pNode = parent;     AVLTreeNode<K, V>* subL = parent->_left;     AVLTreeNode<K, V>* subLR = subL->_right;     int bf = subLR->_bf;       _RotateL(parent->_left);     _RotateR(parent);           if (bf == 1)     {         pNode->_bf = 0;         subL->_bf = -1;     }     else if (bf == -1)     {         pNode->_bf = 1;         subL->_bf = 0;     }     else     {         pNode->_bf = 0;         subL->_bf = 0;     }   }

 右左雙旋:

1. parent只有一個孩子:由於節點subRL的插入破壞了AVL樹的平衡,parent的平衡因子變為2,需要利用旋轉來降低高度!

  • 首先,以subR為軸,將subRL提上去(右旋),保證parent、subR 和 subRL在一條直線上;
  • 以parent為軸,將上一步標記為subRL的節點向上升(左旋),這樣達到了降低高度的目的;
  • 雙旋之後,parent和subR的平衡因子都要置為0

 

2.parent有兩個孩子:沒有插入subLL或者subLR之前的AVL樹一定是處於平衡狀態的,並且滿足AVL樹的性質。

  正是由於插入了節點subLL或者subLR,導致其祖先節點的平衡因子被改變,grandfather的平衡因子變為2,平衡態比打破,需要進行旋轉來降低高度!

  • 首先parent為軸將subL節點往上提至原parent的位置(右旋),將grandfather、parent 和 subL旋至一條直線上;
  • 再以grandfather為軸將subL往上提至grandfather的位置(左旋),此時以subL為根的左右子樹的高度相同,恢復了平衡態!

parent有兩個孩子時,要看插入的節點是subL的右孩子還是左孩子,雙旋後對平衡因子的修改分兩種情況:

  • subL的平衡因子為1,即subL有右孩子無左孩子(有subLR但無subLL),雙旋之後將grandfather的平衡因子置為-1,將parent的平衡因子置為0;
  • subL的平衡因子為-1,即subL有左孩子無右孩子(有subLL但無subLR),雙旋之後將grandfather的平衡因子置為0,將parent的平衡因子置為1; 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 //右左雙旋 template<class K, class V> void AVLTree<K, V>::_RotateRL(AVLTreeNode<K, V>*&  parent) {     AVLTreeNode<K, V>* pNode = parent;     AVLTreeNode<K, V>* subR= parent->_right;     AVLTreeNode<K, V>* subRL = subR->_left;     int bf = subRL->_bf;       _RotateR(parent->_right);     _RotateL(parent);       if (bf == 1)     {         pNode->_bf = 0;         subR->_bf = -1;     }     else if (bf == -1)     {         pNode->_bf = 1;         subR->_bf = 0;     }     else     {         pNode->_bf = 0;         subR->_bf = 0;     } }

Copyright © Linux教程網 All Rights Reserved