一、什么是平衡二叉樹
平衡二叉樹(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] => )
參考書籍:《大話數據結構》