平衡二叉树详解——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] => )
参考书籍:《大话数据结构》