二叉搜索树属于二叉树,具有如下的性质:
1.节点的左子树不为空,那么左子树所有的节点的值都小于该节点的值
2.节点的右子树不为空,那么右子树所有的节点的值都大于该节点的值.
3.节点的左右子树都是二叉搜索树.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2zyxSEF-1594085941066)(evernotecid://8BF4008B-52C0-484E-A4B5-8EBD45F20215/appyinxiangcom/13973009/ENResource/p1200)]
上图就是一个二叉搜索树.
查找逻辑很简单,像极了在有序数组内进行二分查找.
查找时从根节点开始:
(1)如果大于当前节点的值,从右孩子进行查找
(2)如果小于当前节点的值,从左孩子进行查找
递归上述(1)(2)过程,如果找到null或者与查找值相等的节点就返回.
增加和查找逻辑基本一致.
增加时从根节点开始:
(1)如果大于当前节点的值,如果右孩子为空则插入,否则在右孩子递归(1)(2)过程.
(2)如果小于当前节点的值,如果左孩子为空则插入,否则在左孩子递归(1)(2)过程.
删除的情况比较复杂一些:
按照查找的逻辑找到对应的节点,然后分情况处理:
(1)如果删除的节点是椰子节点(无孩子),直接删除.
(2)如果删除的节点有一个孩子,直接将孩子节点替换该节点
(3)如果删除的节点有两个孩子,那么找到右孩子最左的节点后替换该节点并删除右孩子最左接节点(删除 时如果有右孩子,右孩子直接替换).
AVL树是一颗高度平衡的二叉搜索树.对于所有节点的左子树和右子树的高度差不超过1.
不平衡的判断条件为,左子树和右子树的高度差超过1,即为不平衡.
z为第一个从x到root节点的不平衡的节点,y为左或右孩子,x为当前插入的节点.
z,x,y三个节点依次在左侧,需要一次右旋z节点即可平衡.
T1, T2, T3,T4为子树.
z y
/ \ / \
y T4 Right Rotate (z) x z
/ \ - - - - - - - - -> / \ / \
x T3 T1 T2 T3 T4
/ \
T1 T2
z,x,y三个节点依次在右侧,需要一次左旋z节点即可平衡.
z y
/ \ / \
T1 y Left Rotate(z) z x
/ \ - - - - - - - -> / \ / \
T2 x T1 T2 T3 T4
/ \
T3 T4
以上两种case都比较简单,剩下的LR,RL这种case都是由这两种基本操作转换而成
新插入的节点在y的右侧,y为z的左孩子.此时xyz不在节点的一侧,zyx成折线形状,我们要把它变为直线,然后再旋转z.
所以先进行左旋y(z的左孩子),在右旋z节点.
z z x
/ \ / \ / \
y T4 Left Rotate (y) x T4 Right Rotate(z) y z
/ \ - - - - - - - - -> / \ - - - - - - - -> / \ / \
T1 x y T3 T1 T2 T3 T4
/ \ / \
T2 T3 T1 T2
新插入的节点在y的左侧,y为z的右孩子.此时xyz不在节点的一侧,zyx成折线形状,我们要把它变为直线,然后再旋转z.
所以先进行右旋y(z的左孩子),在左旋z节点.
z z x
/ \ / \ / \
T1 y Right Rotate (y) T1 x Left Rotate(z) z y
/ \ - - - - - - - - -> / \ - - - - - - - -> / \ / \
x T4 T2 y T1 T2 T3 T4
/ \ / \
T2 T3 T3 T4
class AVLNode
{
public $key;
public $height = 1;
public $left;
public $right;
public function __construct($key)
{
$this->key = $key;
}
}
class AVLTree
{
public $root;
/**
* @param $node AVLNode
* @return mixed
*/
public function rightRotate($node)
{
$left = $node->left;
$leftRight = $left->right;
$left->right = $node;
$node->left = $leftRight;
$node->height = max($this->height($node->left), $this->height($node->right)) + 1;
$left->height = max($this->height($left->left), $this->height($left->right)) + 1;
return $left;
}
public function height($node)
{
if ($node == null) {
return 0;
}
return $node->height;
}
/**
* @param $node AVLNode
* @return mixed
*/
public function leftRotate($node)
{
$right = $node->right;
$rightLeft = $right->left;
$right->left = $node;
$node->right = $rightLeft;
$node->height = max($this->height($node->left), $this->height($node->right)) + 1;
$right->height = max($this->height($right->left), $this->height($right->right)) + 1;
return $right;
}
public function getBalance($node)
{
if ($node == null) {
return 0;
}
return $this->height($node->left) - $this->height($node->right);
}
public function insert($node, $val)
{
if ($node == null) {
return (new AVLNode($val));
}
if ($val < $node->key) {
$node->left = $this->insert($node->left, $val);
} elseif ($val > $node->key) {
$node->right = $this->insert($node->right, $val);
} else {
return $node;
}
//注意先更新高度此时左子树的高度和右子树的高度可能有变化
$node->height = max($this->height($node->left), $this->height($node->right)) + 1;
$balance = $this->getBalance($node);
//左侧不平衡且插入的值比左树节点的值要小 说明节点的值肯定在左子树的左节点上
if ($balance > 1 && $val < $node->left->key) {
return $this->rightRotate($node);
}
//右侧不平衡且插入的值比右树节点的值要大
if ($balance < -1 && $val > $node->right->key) {
return $this->leftRotate($node);
}
//左侧不平衡且插入的值比左树节点的值要大,当前的值肯定在作数的右节点
if ($balance > 1 && $val > $node->left->key) {
$node->left = $this->leftRotate($node->left);
return $this->rightRotate($node);
}
if ($balance < -1 && $val < $node->right->key) {
$node->right = $this->rightRotate($node->right);
return $this->leftRotate($node);
}
return $node;
}
}
$avlTree = new AVLTree();
$avlTree->root = $avlTree->insert($avlTree->root, 1);
$avlTree->root = $avlTree->insert($avlTree->root, 2);
$avlTree->root = $avlTree->insert($avlTree->root, 3);
$avlTree->root = $avlTree->insert($avlTree->root, 4);
$avlTree->root = $avlTree->insert($avlTree->root, 5);
var_dump($avlTree->root);