二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

  以前只是知道又这么一种树但是没怎么去了解,这次查看了算法导论上介绍的思路, 用php写了个例子。

节点类

BST树类

二叉搜索树样图

下面介绍下大致的操作

一  遍历

二叉搜索树可以通过简单的递归来遍历所有节点的关键词, 根据根,以及左右子树的输出顺序分为3种

(左根右) 中序遍历  [2 3 4 6 7 9 13 15 17 18 20]

(根左右) 先序遍历  [15 6 3 2 4 7 13 9 18 17 20]

(左右根) 后序遍历  [2 4 3 9 13 7 6 17 20 18 15]

中序遍历 示例

    /**
* 遍历节点,获取key数组
* @param Node $node 节点
* @param int $type 遍历类型 0 中序 1 前序 2 后序
* @return array
* @author zxqc2018
*/
public function walkTree(Node $node, int $type = 0)
{
$keyArr = [];
$walkTreeFunc = function (?Node $node) use (&$keyArr, &$walkTreeFunc, $type){
if (!is_null($node)) {
if ($type === 1) {
$keyArr[] = $node->getKey();
$walkTreeFunc($node->getLeft());
$walkTreeFunc($node->getRight());
} else if ($type == 2) {
$walkTreeFunc($node->getLeft());
$walkTreeFunc($node->getRight());
$keyArr[] = $node->getKey();
} else {
$walkTreeFunc($node->getLeft());
$keyArr[] = $node->getKey();
$walkTreeFunc($node->getRight());
}
}
}; $walkTreeFunc($node); return $keyArr;
}

二 查找节点

非递归查找

    /**
* 根据key, 查找节点
* @param int $key
* @param Node|null $node
* @return Node|null
* @author zxqc2018
*/
public function search(int $key, Node $node = null)
{
if (is_null($node)) {
$node = $this->getRoot();
} while (!is_null($node) && $key != $node->getKey()) {
if ($key < $node->getKey()) {
$node = $node->getLeft();
} else {
$node = $node->getRight();
}
} return $node;
}

递归查找

    /**
* 根据key, 查找节点
* @param int $key
* @param Node|null $node
* @return mixed
* @author zxqc2018
*/
public function searchRecursion(int $key, Node $node = null)
{
if (is_null($node)) {
$node = $this->getRoot();
} $recursionFunc = function ($key, Node $node) use (&$recursionFunc) {
if (is_null($node) || $node->getKey() == $key) {
return $node;
} if ($key < $node->getKey()) {
return $recursionFunc($key, $node->getLeft());
} else {
return $recursionFunc($key, $node->getRight());
}
};
return $recursionFunc($key, $node);
}

三 查找最大或小节点

最小节点

    /**
* 查找最小节点
* @param Node|null $node
* @return Node|null
* @author zxqc2018
*/
public function findMinNode(Node $node)
{
if (!is_null($node)) {
while (!is_null($node->getLeft())) {
$node = $node->getLeft();
}
}
return $node;
}

最大节点

    /**
* 查找最大节点
* @param Node|null $node
* @return Node|null
* @author zxqc2018
*/
public function findMaxNode(Node $node)
{
if (!is_null($node) && !is_null($node->getRight())) {
$node = $this->findMaxNode($node->getRight());
}
return $node;
}

四 后继和前驱

一颗二叉搜索树,按照中序遍历(从小到大)后的次序,  给定某个节点, 那么 后继 则是 此节点之后的那个节点, 前驱 则反之

查找后继有两种情况

1 节点的右孩子非空,   则后继是 右节点为根的子树种 关键字 最小的节点 。

2 节点的右孩子是空 并且有后继(树中的最大关键字的节点无后继)。那么 后继是  给点节点 最早有左孩子的底层祖先。

拿上面样图中 13 这个节点的 举例 。13的 第一个祖先 是 7 ,由于 13 是7的右孩子,所以肯定比 7 大,而 7的左孩子也肯定比 13 小 ,  以此类推, 到 6 的时候,是 祖先的 左孩子 , 说明 6 的祖先 肯定 比 13 , 也是祖先中比   13 大的 最小的节点。

后置

    /**
* 获取节点的后继
* @param Node $node
* @return Node|null
* @author zxqc2018
*/
public function getSuccessor(Node $node)
{
//是否有右孩子
if (!is_null($node->getRight())) {
return $this->findMinNode($node->getRight());
} $y = $node->getParent(); //向上逐层判断是否为祖先的右孩子
while (!is_null($y) && $node === $y->getRight()) {
$node = $y;
$y = $y->getParent();
} return $y;
}

前驱

    /**
* 获取节点的前驱
* @param Node $node
* @return Node|null
* @author zxqc2018
*/
public function getPredecessor(Node $node)
{
//是否有左孩子
if (!is_null($node->getLeft())) {
return $this->findMaxNode($node->getLeft());
} $y = $node->getParent(); //向上逐层判断是否为祖先的左孩子
while (!is_null($y) && $node === $y->getLeft()) {
$node = $y;
$y = $y->getParent();
} return $y;
}

五 插入

    /**
* 插入节点key
* @param int $key
* @return Node
* @author zxqc2018
*/
public function insert(int $key)
{
$x = $this->getRoot();
$y = null;
$z = new Node($key); while (!is_null($x)) {
$y = $x;
if ($key < $x->getKey()) {
$x = $x->getLeft();
} else {
$x = $x->getRight();
}
} //设置插入节点的父节点
$z->setParent($y); //假如树还没根节点
if (is_null($y)) {
$this->root = $z;
} else if ($key < $y->getKey()) {
$y->setLeft($z);
} else {
$y->setRight($z);
} return $z;
}

六 删除

删除的情况比较复杂可以分为3种

假如 删除节点  为 z

1) z没有孩子

z的父节点用null 来替换 $z节点

2) z有一个孩子

假如z有一个右孩子,  z的右孩子 替换 z, 并且 z右孩子的父节点指向 z的父节点  ,如下图

3) z有两个孩子

可以找到$z节点的后继或者前驱节点来替换$z, 达到删除,并且不破坏树结构的目的。 这里选后继来举例, 可以分成2种情况

假如 后继节点 为 y

a) z的右孩子就是它的后继节点

y 替换 z 节点,  y的左孩子指向 z 的 左孩子,  z的 左孩子的 父节点指向 y,  y的父节点指向  z 节点的父节点

这里由个情况要说明就是 , z 的 后继节点 的左孩子肯定为null, 假如不是null 的话那么z 的后继就是y的左孩子了, 所以 z的后继 y 肯定是没有左孩子的

b) z的右孩子不是它的后继节点

这情况通过转换下就可以和上面情况一致了,所以只需转换下就OK了

y的右孩子替换 y, y 的右孩子 改成 z 的右孩子,  z 的右孩子的 父节点 由 z 改为 y,  这样转换后  就和上面的情况一致了

为什么可以这样转换?

y的右孩子替换 y,   这操作 等同于 删除y 节点 操作

y改为 z 的 右孩子的 父亲,  因为 y 是z 的后继 所以  y 肯定是 z 的右边 子树 中最小的,  所以   y 可以 作为  z 的 右孩子的父亲 , 没有破坏  树的结构

删除代码

    /**
* 移动节点
* @param Node $src 源节点
* @param Node $dst 目标节点
* @author zxqc2018
*/
protected function transplantNode(?Node $src, Node $dst)
{
if (is_null($dst->getParent())) {
$this->root = $src;
}else if ($dst === $dst->getParent()->getLeft()) {
$dst->getParent()->setLeft($src);
} else {
$dst->getParent()->setRight($src);
} //源节点不空,则把源节点父节点指向目标节点的父节点
if (!is_null($src)) {
$src->setParent($dst->getParent());
}
} /**
* 删除节点
* @param Node $node
* @author zxqc2018
*/
public function delete(Node $node)
{
if (is_null($node->getLeft())) {
$this->transplantNode($node->getRight(), $node);
} else if (is_null($node->getRight())) {
$this->transplantNode($node->getLeft(), $node);
} else {
$successorNode = $this->getSuccessor($node);
//删除节点的右孩子不是后继节点,则做相应转换
if ($node->getRight() !== $successorNode) {
//后继节点的右孩子替换后继节点
$this->transplantNode($successorNode->getRight(), $successorNode);
//设置删除节点的右孩子为后继节点的右孩子
$successorNode->setRight($node->getRight());
//删除节点的右孩子的父节点改为后继节点
$successorNode->getRight()->setParent($successorNode);
} //后继节点替换删除节点
$this->transplantNode($successorNode, $node);
//设置删除节点的左孩子为后继节点的左孩子
$successorNode->setLeft($node->getLeft());
//删除节点的左孩子的父节点改为后继节点
$successorNode->getLeft()->setParent($successorNode);
}
}

代码地址

https://github.com/zxqc/Share

二叉搜索树-php实现 插入删除查找等操作的更多相关文章

  1. 二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历

    二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历   二叉搜索树的结构(30 分) 二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则 ...

  2. 二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历

    二叉搜索树的结构(30 分) 二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值:若它的右子树不空,则右子树上所有结点的值均大于它的根 ...

  3. 高度平衡的二叉搜索树(AVL树)

    AVL树的基本概念 AVL树是一种高度平衡的(height balanced)二叉搜索树:对每一个结点x,x的左子树与右子树的高度差(平衡因子)至多为1. 有人也许要问:为什么要有AVL树呢?它有什么 ...

  4. Java二叉搜索树实现

    树集合了数组(查找速度快)和链表(插入.删除速度快)的优点 二叉树是一种特殊的树,即:树中的每个节点最多只能有两个子节点 二叉搜索树是一种特殊的二叉树,即:节点的左子节点的值都小于这个节点的值,节点的 ...

  5. 二叉搜索树的java实现

    转载请注明出处 一.概念 二叉搜索树也成二叉排序树,它有这么一个特点,某个节点,若其有两个子节点,则一定满足,左子节点值一定小于该节点值,右子节点值一定大于该节点值,对于非基本类型的比较,可以实现Co ...

  6. 自己动手实现java数据结构(六)二叉搜索树

    1.二叉搜索树介绍 前面我们已经介绍过了向量和链表.有序向量可以以二分查找的方式高效的查找特定元素,而缺点是插入删除的效率较低(需要整体移动内部元素):链表的优点在于插入,删除元素时效率较高,但由于不 ...

  7. 二叉搜索树 C语言实现

    1.二叉搜索树基本概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是一棵具有如下特性的非空二叉树: (1)若它的左子树非空,则左子树上所有结点的关键字均小于根结点的关键字: (2)若它的右子树非 ...

  8. java二叉搜索树原理与实现

    计算机里面的数据结构 树 在计算机存储领域应用作用非常大,我之前也多次强调多磁盘的存取速度是目前计算机飞速发展的一大障碍,计算机革命性的的下一次飞跃就是看硬盘有没有质的飞跃,为什么这么说?因为磁盘是永 ...

  9. 算法导论 第十二章 二叉搜索树(python)

    上图: 这是二叉搜索树(也有说是查找树的)基本结构:如果y是x的左子树中的一个结点,那么y.key <= x.key(如a图中的6根结点大于它左子树的每一个结点 6 >= {2,5,5}) ...

随机推荐

  1. 2013年第四届蓝桥杯国赛 九宫重排(HashMap+双BFS优化)

    九宫重排     时间限制:1.0s   内存限制:256.0MB 问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可以移动到空格中.经过若干 ...

  2. 在Tomcat启动时直接创建servlet(二)

  3. Java 表达式解析(非原创)

    因项目需要,在网上找来一套表达式解析方法,由于原来的方法太过于零散,不利于移植,现在整理在同一文件内: 文件中包含5个内部类,源码如下: import java.util.ArrayList; imp ...

  4. Unity串口通信

    一.串口简介 串行接口(串口)通常指COM接口,是采用串行通信方式的扩展接口.串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接 ...

  5. jmeter 阶梯式加压测试

    性能测试中,有时需要模拟一种实际生产中经常出现的情况,即:从某个值开始不断增加压力,直至达到某个值,然后持续运行一段时间. 在jmeter中,有这样一个插件,可以帮我们实现这个功能,这个插件就是:St ...

  6. P4088 [USACO18FEB]Slingshot 线段树+扫描线

    \(\color{#0066ff}{ 题目描述 }\) Farmer John最讨厌的农活是运输牛粪.为了精简这个过程,他产生了一个新奇的想法:与其使用拖拉机拖着装满牛粪的大车从一个地点到另一个地点, ...

  7. Please tell me who you are. Run git config --global user.email "you@example.com" git config --global user.name "Your Name"

    解决办法

  8. tomcat启动优化

    tomcat的最佳实践运行模式 Tomcat Connector三种运行模式(BIO, NIO, APR)的比较和优化. org.apache.coyote.http11.Http11Protocol ...

  9. php 多语言(UTF-8编码)导出Excel、CSV乱码解决办法之导出UTF-8编码的Excel、CSV

    新项目,大概情况是这样的:可能存在多国.不同语种使用者,比喻有中文.繁体中文,韩文.日本等等,开发时选择了UTF-8编码,开发顺利,没有问题.昨天做了一个csv导出功能,导出的东西完全乱了: 设置mb ...

  10. PHPExcel 报 Allowed memory size of 8388608 byte

    使用 phpExcel 报 Allowed memory size of 8388608 bytes exhausted 错误,原因是php页面消耗的最大内存默认是为 8M (在PHP的ini件里可以 ...