平衡二叉樹詳解——PHP代碼實現


一、什么是平衡二叉樹

平衡二叉樹(Self-Balancing Binary Search Tree 或者 Height-Balancing Binary Search Tree)譯為 自平衡的二叉查找樹或者高度平衡的二叉查找樹,簡稱平衡二叉樹,也叫 AVL 樹,是一種二叉排序樹。每個節點的左子樹和右子樹的高度差至多等於 1,我們將二叉樹上結點的左子樹深度減去右子樹深度的值稱為平衡因子 BF(Balance Factor),那么平衡二叉樹上所有結點的平衡因子只可能是  -1,0,1。只要樹上有結點的平衡因子的絕對值大於 1,則該二叉樹就是不平衡的。

下面舉四個例子:

  • 圖1不滿足平衡二叉樹定義,58和88這兩個結點的平衡因子BF分別是2和-2,不是平衡二叉樹
  • 圖2不是平衡二叉樹,因為平衡二叉樹首要要是二叉排序樹,59比58大卻是58的左子樹,這是不符合二叉排序樹的定義的
  • 圖3不滿足平衡因子小於等於1的要求,對58這個節點來說,平衡因子BF的值是3,因而不是平衡二叉樹
  • 圖4滿足平衡二叉樹的定義,是平衡二叉樹

二、平衡二叉樹的實現原理

平衡二叉樹構建的基本思想就是在構建二叉排序樹的過程中,每當插入一個結點時,先檢查是否因插入而破壞了樹的平衡性,若是,則找出 最小不平衡子樹。在保持二叉排序樹特性的前提下,調整最小不平衡子樹中各結點之間的鏈接關系,進行相應的 旋轉(左旋或右旋),使之成為新的平衡子樹。 

最小不平衡子樹

距離插入結點最近的,且平衡因子的絕對值大於1的結點為根的子樹,我們稱為最小不平衡子樹。

如下圖,當新插入結點37時,距離它最近的平衡因子絕對值超過1的結點是58 (即它的左子樹高度2減去右子樹高度0),所以從58開始以下的子樹為最小不平衡子樹。

左旋/右旋

  • 當右子樹比左子樹高,即平衡因子小於-1,需要進行左旋,如下圖

 

  • 當右子樹比左子樹低,即平衡因子大於1,需要進行右旋,如下圖

實例

 假設插入節點的順序是{3,2,1,4,5,6,7,10,9,8}

 根據二叉排序樹的特性,我們通常會將它構建成如下圖1的樣子。雖然它完全符合二叉排序樹的定義,但是對這樣高度達到8的二叉樹查找是非常不利的。我們更期望能構建成下圖2的樣子,這樣才能提供高效的查詢效率。

 

下面就開始構建上圖2

 對於{3,2,1,4,5,6,7,10,9,8}的前兩位3和2,我們正常的構建,到了第三個數1時發現根節點的平衡因子變成了2,需要把以 3 為根節點的子樹進行右旋

 

 插入第四個節點 4 的時候,左右子樹高度為 -1,符合平衡二叉樹要求,繼續插入第五個節點,此時又不符合平衡二叉樹的要求了,這個時候右子樹比較高,需要左旋:旋轉的時候以最小不平衡子樹為單位,此時最小的不平衡子樹是3、4、5節點構成的子樹,我們以4為中心進行左旋

 

繼續增加節點,當插入節點 6 時,發現根節點 2 上維護的高度差值為 -2,又不滿足平衡二叉樹了,這個時候,需要以 2 為中心對樹進行左旋:如下圖所示(右子樹中旋轉到根節點的節點對應子樹需要移到旋轉后二叉樹的左子樹中):

增加結點7,同樣的左旋,使得整棵樹達到平衡

 繼續增加節點 10,結構無變化。再插入節點 9,發現結點7的BF變成-2又需要調整。但是這次調整需要繞個彎,不能簡單的進行簡單的左旋,需要先將以10作為根節點的子樹做一次右轉,再將以7為根節點的子樹做一次左轉,讓這棵不平衡子樹轉化為平衡子樹

最后,插入節點8,此時情況和剛才類似,這個時候,我們以 9 為根節點對子樹進行右旋,再以6為根節點對子樹進行左旋,最終達到平衡狀態

相信大家應該有點明白,所謂的平衡二叉樹,其實就是在二叉排序樹創建過程中保證它的平衡性,一旦發現有不平衡的情況,馬上處理,這樣就不會造成不可收拾的情況出現。

通過剛才這個例子,你會發現,當最小不平衡子樹根結點的平衡因子BF是大於1時,就右旋,小於-1時就左旋 

三、平衡二叉樹PHP代碼實現

平衡二叉樹結點類

 1 <?php
 2 /**
 3  * AVLNode.php
 4  * Created on 2019/4/27 16:44
 5  * Created by Wilin
 6  */
 7 
 8 class AVLNode
 9 {
10     public $data;
11     public $left = null;
12     public $right = null;
13     public $bf = 0;
14     public $parent = null;
15 
16     public function __construct($data) {
17         $this->data = $data;
18     }
19 }

 

 中序遍歷

 1 <?php
 2 /**
 3  * Traverse.php 遍歷
 4  * Created on 2019/4/27 11:10
 5  * Created by Wilin
 6  */
 7 function midOrderTraverse($tree) {
 8     if($tree == null) {
 9         return;
10     }
11 
12     midOrderTraverse($tree->left);
13     printf("%s\n", $tree->data);
14     midOrderTraverse($tree->right);
15 }

 

平衡二叉樹

  1 <?php
  2 /**
  3  * AVLTree.php
  4  * Created on 2019/4/27 16:51
  5  * Created by Wilin
  6  */
  7 
  8 include "AVLNode.php";
  9 include "../Traverse.php";
 10 
 11 class AVLTree
 12 {
 13     private $root;
 14 
 15     const LH = 1;
 16     const EH = 0;
 17     const RH = -1;
 18 
 19     public function getTree() {
 20         return $this->root;
 21     }
 22 
 23     public function insert(int $data) {
 24         $this->insert_node($data, $this->root);
 25     }
 26 
 27     /**
 28      * 插入節點
 29      * @param int $data
 30      * @param $tree
 31      * @return bool 是否需要調整樹結構,true:是,false:否
 32      */
 33     protected function insert_node(int $data, &$tree) {
 34 
 35         //創建節點
 36         if (!$tree) {
 37             $tree = new AVLNode($data);
 38             $tree->bf = self::EH;
 39             return true;                            //插入成功之后需要判斷是否需要調整
 40         }
 41 
 42         if ($data < $tree->data) {
 43             //遞歸插入節點
 44             if (!$this->insert_node($data, $tree->left)) {
 45                 return false;
 46             } else {
 47                 //更正新插入節點對父節點的指向
 48                 if (empty($tree->left->parent)) {
 49                     $tree->left->parent = $tree;
 50                 }
 51                 //判斷是否需要調整子樹
 52                 switch ($tree->bf) {
 53                     case self::LH:                  //左子樹偏高,需要對左子樹進行調整
 54                         $this->left_balance($tree);
 55                         return false;               //已經進行過調整,不需要繼續調整
 56                     case self::EH:
 57                         $tree->bf = self::LH;
 58                         return true;                //由等高變為左高,樹的整體高度發生變化,需要繼續判斷上層節點是否需要調整
 59                     case self::RH:
 60                         $tree->bf = self::EH;
 61                         return false;               //由右高變為等高,樹的整體高度沒有發生變化,不需要調整
 62                 }
 63             }
 64         } else {
 65             if (!$this->insert_node($data,$tree->right)) {
 66                 return false;
 67             } else {
 68                 if (empty($tree->right->parent)) {
 69                     $tree->right->parent = $tree;
 70                 }
 71                 switch ($tree->bf) {
 72                     case self::LH:
 73                         $tree->bf = self::EH;
 74                         return false;
 75                     case self::EH:
 76                         $tree->bf = self::RH;
 77                         return true;
 78                     case self::RH:
 79                         $this->right_balance($tree);
 80                         return false;
 81                 }
 82             }
 83         }
 84     }
 85 
 86     /**
 87      * 右旋
 88      * @param $tree
 89      */
 90     protected function right_rotate(&$tree) {
 91         //修改父節點與子樹之間的指向時需要特別注意根節點
 92 
 93         $subTree = $tree->left;
 94         //修改子樹對父節點的指向
 95         if ($tree->parent) {
 96             $subTree->parent = $tree->parent;
 97             $left = false;              //調整之前記錄當前調整的子樹是父節點的左子樹還是右子樹
 98             if($tree->parent->left == $tree){
 99                 $left = true;
100             }
101         } else {
102             $subTree->parent = null;    //根節點的父節點為空
103         }
104         //交換節點位置
105         $tree->left = $subTree->right;
106         $tree->parent = $subTree;
107         $subTree->right = $tree;
108 
109         $tree = $subTree;
110         //修改父節點對子樹的指向
111         if (!$tree->parent) {
112             $this->root = $tree;
113         } else {
114             if ($left) {
115                 $tree->parent->left = $tree;
116             } else {
117                 $tree->parent->right = $tree;
118             }
119         }
120     }
121 
122     /**
123      * 左旋
124      * @param $tree
125      */
126     protected function left_rotate(&$tree) {
127 
128         $subTree = $tree->right;
129         if ($tree->parent) {
130             $subTree->parent  = $tree->parent;
131             $left = true;
132             if ($tree->parent->right == $tree) {
133                 $left = false;
134             }
135         } else {
136             $subTree->parent = null;
137         }
138 
139         $tree->right = $subTree->left;
140         $tree->parent = $subTree;
141         $subTree->left = $tree;
142         $tree = $subTree;
143         if (!$tree->parent) {
144             $this->root = $tree;
145         } else {
146             if ($left) {
147                 $tree->parent->left = $tree;
148             } else {
149                 $tree->parent->right = $tree;
150             }
151         }
152     }
153 
154     /**
155      * 調整左子樹
156      * @param $tree
157      */
158     protected function left_balance(&$tree) {
159         $subTree = $tree->left;
160         switch ($subTree->bf) {
161             case self::LH:
162                 $tree->bf = $subTree->bf = self::EH;            //先修改平衡因子,再進行旋轉
163                 $this->right_rotate($tree);
164                 break;
165             case self::RH:
166                 $subTree_r = $subTree->right;
167                 switch ($subTree_r->bf) {
168                     case self::LH:
169                         $tree->bf = self::RH;
170                         $subTree->bf = self::EH;
171                         break;
172                     case self::RH:
173                         $tree->bf = self::EH;
174                         $subTree->bf = self::LH;
175                         break;
176                 }
177                 $subTree_r->bf = self::EH;
178                 $this->left_rotate($subTree);
179                 $this->right_rotate($tree);
180                 break;
181         }
182     }
183 
184     /**
185      * 調整右子樹
186      * @param $tree
187      */
188     protected function right_balance(&$tree) {
189         $subTree = $tree->right;
190         switch ($subTree->bf) {
191             case self::RH:
192                 $tree->bf = $subTree->bf = self::EH;
193                 $this->left_rotate($tree);
194                 break;
195             case self::LH:
196                 $subTree_l = $subTree->left;
197                 switch ($subTree_l->bf) {
198                     case self::RH:
199                         $tree->bf = self::LH;
200                         $subTree->bf = self::EH;
201                         break;
202                     case self::EH:
203                         $tree->bf = $subTree->bf = self::EH;
204                         break;
205                     case self::LH:
206                         $tree->bf = self::EH;
207                         $subTree->bf = self::RH;
208                         break;
209                 }
210                 $subTree_l->bf = self::EH;
211                 $this->right_rotate($subTree);
212                 $this->left_rotate($tree);
213         }
214     }
215 }
216 
217 $avlTree = new AVLTree();
218 $avlTree->insert(3);
219 $avlTree->insert(2);
220 $avlTree->insert(1);
221 $avlTree->insert(4);
222 $avlTree->insert(5);
223 $avlTree->insert(6);
224 $avlTree->insert(7);
225 $avlTree->insert(10);
226 $avlTree->insert(9);
227 $avlTree->insert(8);
228 midOrderTraverse($avlTree->getTree());
229 print_r($avlTree->getTree());

 

打印結果如下

E:\www\tree\2>php AVLTree.php
1
2
3
4
5
6
7
8
9
10
AVLNode Object
(
    [data] => 4
    [left] => AVLNode Object
        (
            [data] => 2
            [left] => AVLNode Object
                (
                    [data] => 1
                    [left] =>
                    [right] =>
                    [bf] => 0
                    [parent] => AVLNode Object
 *RECURSION*
                )

            [right] => AVLNode Object
                (
                    [data] => 3
                    [left] =>
                    [right] =>
                    [bf] => 0
                    [parent] => AVLNode Object
 *RECURSION*
                )

            [bf] => 0
            [parent] => AVLNode Object
 *RECURSION*
        )

    [right] => AVLNode Object
        (
            [data] => 7
            [left] => AVLNode Object
                (
                    [data] => 6
                    [left] => AVLNode Object
                        (
                            [data] => 5
                            [left] =>
                            [right] =>
                            [bf] => 0
                            [parent] => AVLNode Object
 *RECURSION*
                        )

                    [right] =>
                    [bf] => 1
                    [parent] => AVLNode Object
 *RECURSION*
                )

            [right] => AVLNode Object
                (
                    [data] => 9
                    [left] => AVLNode Object
                        (
                            [data] => 8
                            [left] =>
                            [right] =>
                            [bf] => 0
                            [parent] => AVLNode Object
 *RECURSION*
                        )

                    [right] => AVLNode Object
                        (
                            [data] => 10
                            [left] =>
                            [right] =>
                            [bf] => 0
                            [parent] => AVLNode Object
 *RECURSION*
                        )

                    [bf] => 0
                    [parent] => AVLNode Object
 *RECURSION*
                )

            [bf] => 0
            [parent] => AVLNode Object
 *RECURSION*
        )

    [bf] => -1
    [parent] =>
)

參考書籍:《大話數據結構》

其他參考:https://articles.zsxq.com/id_dgm8kpxzw4xo.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM