数据结构-二叉搜索树与AVL树

二叉搜索树

二叉搜索树属于二叉树,具有如下的性质:
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树

AVL树是一颗高度平衡的二叉搜索树.对于所有节点的左子树和右子树的高度差不超过1.

不平衡的判断条件

不平衡的判断条件为,左子树和右子树的高度差超过1,即为不平衡.

左旋右旋操作

z为第一个从x到root节点的不平衡的节点,y为左或右孩子,x为当前插入的节点.

LL

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

RR

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都是由这两种基本操作转换而成

LR

新插入的节点在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

RL

新插入的节点在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

源码

<?php

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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值