这篇文章主要介绍一下 平衡二叉树(AVL)
,对于 二分搜索树
来说,如果树上的 元素
是顺序
添加的,会导致数据退化成一个 链表
,这样就会造成很严重的性能问题,此时就需要在 二分搜索树
的基础上,保证元素插入时平衡
,在了解 AVL
之前,需要您对 二分搜索树
有一定的了解,可以参考之前的文章。
1.二分搜索树的问题
如下图所示,若向二分搜索树
中添加的元素是按照 顺序
从小到大依次插入的,这样会导致最后节点退化成链表
:
Tips:反过来依次从大到插入元素也会出现退化成
链表
。
2.二叉树节点高度标注示意图
Tips:平衡因子等于左儿子高度减去右儿子高度,如
45
和这个节点平衡因子-2
。
3.平衡二叉树特点
对于任何一个节点,
左儿子树
和右儿子树
的高度差(Δh)
的绝对值不能超过1
,高度差(Δh)
称为平衡因子
。本质是带了平衡功能的
二分搜索树
。平衡二叉树(AVL) 的
高度(h)
和节点数
之间的关系是O(logn)
级别的。
4.节点定义 PHP 代码
对于 二分搜索树
来说,AVL
需要在插入元素的时候保证 平衡
,就需要引入节点高度(h)
,所以节点定义需要在 二分搜索树
节点定义的基础上增加一个 height
属性:
class AVLNode{
public $e;
public $left = null;
public $right = null;
public $height = 1;
/**
* 构造函数 初始化节点数据
* Node constructor.
* @param $e
*/
public function __construct($e){
$this->e = $e;
}
}
5.计算平衡因子
平衡因子等于左儿子高度减去右儿子高度,代码如下:
/**
* 获取 AVL 节点的高度 h
* @param AVLNode $node
* @return int
*/
private function getHeight(AVLNode $node){
if ($node == null) {
return 0;
}
return $node->height;
}
/**
* 获取节点平衡因子
* @param AVLNode $node
* @return int\
*/
private function getBalanceFactor(AVLNode $node){
if ($node == null){
return 0;
}
return $this->getHeight($node->left) - $this->getHeight($node->right);
}
6.更新节点高度
若递归插入元素时,需要更新节点高度
,递归时当前节点高度 h = 1 + (左右儿子中最大h)
,代码如下:
/**
* 向 AVL 添加元素
* @param $e
*/
public function add($e){
$this->root = $this->recursionAdd($this->root, $e);
}
/**
* 递归向 AVL 添加元素
* @param Node $root
* @param $e
*/
private function recursionAdd($root, $e){
if ($root == null) { //若节点为空则添加元素 并且返回当前节点信息
$root = new AVLNode($e);
$this->size++;
} elseif ($e < $root->e) { //若元素小于当前节点元素 则向左节点递归添加元素
$root->left = $this->recursionAdd($root->left, $e);
} elseif ($e > $root->e) { //若元素大于当前节点元素 则向右节点递归添加元素
$root->right = $this->recursionAdd($root->right, $e);
} //若元素等于当前节点元素 则什么都不做
//更新节点高度
$root->height = 1 + ($this->getHeight($root->left) > $this->getHeight($root->right) ? $this->getHeight($root->left) : $this->getHeight($root->right));
return $root;
}
Tips:此处代码直接贴出之前
二分搜索树
代码添加元素方法。
7.判断二叉树是否为二分搜索树
若要判断二叉树
是否为二分搜索树
,则需要将二叉树中序遍历
,若输出结果是按照顺序依次从小到大排列的,则表示该 二叉树
是一颗 二分搜索树
,采用递归思想层序遍历然后将节点元素依次入队
,代码如下,然后依次出队
查看出队
元素是否依次增大:
/**
* 判断二叉树是否为二分搜索树
*/
public function isBST(){
$queue = new QueueByLinkedList();
$this->inOrder($this->root, $queue);
do {
$e = $queue->dequeue();
if ($queue->getSize() > 0 && $e > $queue->getFront()) {
return false;
}
} while ($queue->getSize() > 0);
return true;
}
/**
* 中序遍历二分搜索树
*/
private function inOrder($root, $queue){