PHP二叉树(一):二叉搜索树

关于二叉搜索树的原理网上的资源就挺多的,而且情况有点小复杂,所以在这里我就不再陈述了,直接上代码吧:

/**
 * author:Meng
 * time:2017/10/19
 * description: 二叉查找树
 */
//结点
class Node{
    public $key;
    public $parent;
    public $left;
    public $right;

public function __construct($key)
{
    $this->key = $key;
    $this->parent = NULL;
    $this->left = NULL;
    $this->right = NULL;
}

}

//二叉搜索树

class Bst{ public $root; /** * 初始化树结构 * @param $arr 初始化树结构的数组 * @return null */ public function init($arr) { $this->root = new Node($arr[0]); for ($i = 1; $i < count($arr); $i++) { $this->Insert($arr[$i]); } }

/**
 * (对内)中序遍历
 * @param $root (树或子树的)根节点
 * @return null
 */
private function mid_order($root)
{
    if ($root != NULL) {
        $this-&gt;mid_order($root-&gt;left);
        echo $root-&gt;key . " ";
        $this-&gt;mid_order($root-&gt;right);
    }
}

/**
 * (对外)中序遍历
 * @param null
 * @return null
 */
public function MidOrder()
{
    $this-&gt;mid_order($this-&gt;root);
}

/**
 * 查找树中是否存在$key对应的节点
 * @param $key 待搜索数字
 * @return $key对应的节点
 */
function search($key)
{
    $current = $this-&gt;root;
    while ($current != NULL) {
        if ($current-&gt;key == $key) {
            return $current;
        } elseif ($current-&gt;key &gt; $key) {
            $current = $current-&gt;left;
        } else {
            $current = $current-&gt;right;
        }
    }
    return $current;
}

/**
 * 查找树中的最小关键字
 * @param $root 根节点
 * @return 最小关键字对应的节点
 */
function search_min($root)
{
    $current = $root;
    while ($current-&gt;left != NULL) {
        $current = $current-&gt;left;
    }
    return $current;
}

/**
 * 查找树中的最大关键字
 * @param $root 根节点
 * @return 最大关键字对应的节点
 */
function search_max($root)
{
    $current = $root;
    while ($current-&gt;right != NULL) {
        $current = $current-&gt;right;
    }
    return $current;
}

/**
 * 查找某个$key在中序遍历时的直接前驱节点
 * @param $x 待查找前驱节点的节点引用
 * @return 前驱节点引用
 */
function predecessor($x)
{
    //左子节点存在,直接返回左子节点的最右子节点
    if ($x-&gt;left != NULL) {
        return $this-&gt;search_max($x-&gt;left);
    }
    //否则查找其父节点,直到当前结点位于父节点的右边
    $p = $x-&gt;parent;
    //如果x是p的左孩子,说明p是x的后继,我们需要找的是p是x的前驱
    while ($p != NULL &amp;&amp; $x == $p-&gt;left) {
        $x = $p;
        $p = $p-&gt;parent;
    }
    return $p;
}

/**
 * 查找某个$key在中序遍历时的直接后继节点
 * @param $x 待查找后继节点的节点引用
 * @return 后继节点引用
 */
function successor($x)
{
    if ($x-&gt;right != NULL) {
        return $this-&gt;search_min($x-&gt;right);
    }
    $p = $x-&gt;parent;
    while ($p != NULL &amp;&amp; $x == $p-&gt;right) {
        $x = $p;
        $p = $p-&gt;parent;
    }
    return $p;
}

/**
 * 将$key插入树中
 * @param $key 待插入树的数字
 * @return null
 */
function Insert($key)
{
    if (!is_null($this-&gt;search($key))) {
        throw new Exception('结点' . $key . '已存在,不可插入!');
    }
    $root = $this-&gt;root;
    $inode = new Node($key);
    $current = $root;
    $prenode = NULL;
    //为$inode找到合适的插入位置
    while ($current != NULL) {
        $prenode = $current;
        if ($current-&gt;key &gt; $inode-&gt;key) {
            $current = $current-&gt;left;
        } else {
            $current = $current-&gt;right;
        }
    }

    $inode-&gt;parent = $prenode;
    //如果$prenode == NULL, 则证明树是空树
    if ($prenode == NULL) {
        $this-&gt;root = $inode;
    } else {
        if ($inode-&gt;key &lt; $prenode-&gt;key) {
            $prenode-&gt;left = $inode;
        } else {
            $prenode-&gt;right = $inode;
        }
    }
    //return $root;
}

/**
 * 在树中删除$key对应的节点
 * @param $key 待删除节点的数字
 * @return null
 */
function Delete($key)
{
    if (is_null($this-&gt;search($key))) {
        throw new Exception('结点' . $key . "不存在,删除失败!");
    }
    $root = $this-&gt;root;
    $dnode = $this-&gt;search($key);
    if ($dnode-&gt;left == NULL || $dnode-&gt;right == NULL) { #如果待删除结点无子节点或只有一个子节点,则c = dnode
        $c = $dnode;
    } else { #如果待删除结点有两个子节点,c置为dnode的直接后继,以待最后将待删除结点的值换为其后继的值
        $c = $this-&gt;successor($dnode);
    }

    //无论前面情况如何,到最后c只剩下一边子结点
    if ($c-&gt;left != NULL) {
        $s = $c-&gt;left;
    } else {
        $s = $c-&gt;right;
    }

    if ($s != NULL) { #将c的子节点的父母结点置为c的父母结点,此处c只可能有1个子节点,因为如果c有两个子节点,则c不可能是dnode的直接后继
        $s-&gt;parent = $c-&gt;parent;
    }

    if ($c-&gt;parent == NULL) { #如果c的父母为空,说明c=dnode是根节点,删除根节点后直接将根节点置为根节点的子节点,此处dnode是根节点,且拥有两个子节点,则c是dnode的后继结点,c的父母就不会为空,就不会进入这个if
        $this-&gt;root = $s;
    } else if ($c == $c-&gt;parent-&gt;left) { #如果c是其父节点的左右子节点,则将c父母的左右子节点置为c的左右子节点
        $c-&gt;parent-&gt;left = $s;
    } else {
        $c-&gt;parent-&gt;right = $s;
    }

    #如果c!=dnode,说明c是dnode的后继结点,交换c和dnode的key值
    if ($c != $dnode) {
        $dnode-&gt;key = $c-&gt;key;
    }
    #返回根节点
//        return $root;
}

/**
 * (对内)获取树的深度
 * @param $root 根节点
 * @return 树的深度
 */
private function getdepth($root)
{
    if ($root == NULL) {
        return 0;
    }

    $dl = $this-&gt;getdepth($root-&gt;left);
    $dr = $this-&gt;getdepth($root-&gt;right);

    return ($dl &gt; $dr ? $dl : $dr) + 1;
}

/**
 * (对外)获取树的深度
 * @param null
 * @return null
 */
public function Depth()
{
    return $this-&gt;getdepth($this-&gt;root);
}

}

调试的时候你们可以调用中序遍历来做,我在上一篇博客中提供了PHP实现的二叉树图形化,有了视觉上的帮助就能更好的帮助我们进行调试,详细大家可以访问我的上一篇博客:《利用PHP实现二叉树的图形显示》

联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2024

备案号:京ICP备15003423号-3