二叉查找树(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. python 中的异常处理与种类

    异常处理是Python工程中补课避免的,进行异常处理,可以帮我们调试代码,使代码找起问题更加简单,更加容易哦. 一般都是利用Try,比较简单,代码也不复杂. try: print 'try...' r ...

  2. register的功能

    register:这个关键字请求编译器尽可能 的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率.这里注意是尽可能,不是绝对.你想想,一个CPU拥有 的寄存器也就那么几个或几十个,你要 ...

  3. linux源码安装apache

    apache安装之前,需要安装APR.APR-Util和PCRE依赖包 下载 Apache     下载地址: http://httpd.apache.org/download.cgi   (打开找最 ...

  4. charles 抓取app https 请求

    测试需要抓取app的https请求链接,百度了一下教程,能设置的都设置成功了,但就是抓取不成功,显示如下图 无奈之下还是用谷歌搜索了下(网速极慢),但是庆幸的找到了问题的答案,原因还是手机设置的问 打 ...

  5. Django会话,用户和注册之cookie

    HTTP状态和TCP不一样,HTTP是无状态的,也就是这一次请求和下一次请求之间没有任何状态保持,我们无法根据请求例如IP来识别是否在同一人的连续性请求.就像我们在访问网站的时候,输入了用户名和密码, ...

  6. linux文件系统相关资料

             linux下文件系统通常是通过虚拟文件系统(VFS)蔽下层具体文件系统操作的差异,为上层的操作提供一个统一的接口.文件系统底层都是用系统IO缓存层提供的块读写接口,实现逻辑块到物理块 ...

  7. (Python OpenGL)【 0】关于VAO和VBO以及OpenGL新特性

    (Python OpenGL)关于新版OpenGL需要了解的: 随着OpenGL状态和固定管线模式的移除,我们不在用任何glEnable函数调用,而且也不会有glVertex.glColor等函数调用 ...

  8. P3750 [六省联考2017]分手是祝愿 期望DP

    \(\color{#0066ff}{ 题目描述 }\) Zeit und Raum trennen dich und mich. 时空将你我分开. B 君在玩一个游戏,这个游戏由 \(n\) 个灯和 ...

  9. 10大Python开源项目推荐(Github平均star2135)

    翻译 | suisui 来源 | 人工智能头条(AI_Thinker) 继续假日充电系列~本文是 Mybridge 挑选的 10 个 Python 开源项目,Github 平均star 2135,希望 ...

  10. CF580C Kefa and Park dfs

    Kefa decided to celebrate his first big salary by going to the restaurant. He lives by an unusual pa ...