技术标签: 二叉排序树 二叉树 二叉搜索树 数据结构与算法 二叉查找树 数据结构
二叉查找树又叫 二叉排序树、二叉搜索树
文章中树的概念和二叉树的定义转自二叉查找树(一)之 图文解析 和 C语言的实现
前驱节点和后继节点 参考:二叉搜索树的前驱节点和后继节点
删除节点参考:二叉查找树 - 删除节点 详解(Java实现)
本文对前驱节点、后继节点、删除操作进行着重讲解
完整代码是C#实现
public class Node
{
public int Key;
public Node Parent;//parent
public Node L; //left
public Node R; //right
}
public class BSTree
{
//树的根节点
public Node Root;
}
这里列举 前序遍历、中序遍历、后序遍历、层次遍历、Z形(蛇形)遍历。
二叉搜索树的中序遍历是单调递增的
public void Preorder(Node n)
{
if (n == null)
return;
Print(n);
Preorder(n.L);
Preorder(n.R);
}
/// <summary>
/// 中序遍历
/// </summary>
/// <param name="n"></param>
public void Inorder(Node n)
{
if (n == null)
return;
Inorder(n.L);
Print(n);
Inorder(n.R);
}
二叉搜索树的中序遍历是单调递增的
/// <summary>
/// 后序遍历
/// </summary>
/// <param name="n"></param>
public void Postorder(Node n)
{
if (n == null)
return;
Postorder(n.L);
Postorder(n.R);
Print(n);
}
/// <summary>
/// 层次遍历
/// </summary>
/// <param name="n"></param>
public void Levelorder(Node n)
{
if (n == null)
return;
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(n);
while(queue.Count>0)
{
var item = queue.Dequeue();
if (item == null)
continue;
Print(item);
queue.Enqueue(item.L);
queue.Enqueue(item.R);
}
}
/// <summary>
/// Z形(蛇形)遍历
/// </summary>
/// <param name="n"></param>
public void ZLevelorder(Node n)
{
Stack<Node> stackl2r = new Stack<Node>();
Stack<Node> stackr2l = new Stack<Node>();
stackl2r.Push(n);
while(stackl2r.Count>0|| stackr2l.Count > 0)
{
while (stackl2r.Count>0)
{
n = stackl2r.Pop();
if (n == null)
continue;
Print(n);
stackr2l.Push(n.L);
stackr2l.Push(n.R);
}
while (stackr2l.Count > 0)
{
n = stackr2l.Pop();
if (n == null)
continue;
Print(n);
stackl2r.Push(n.R);
stackl2r.Push(n.L);
}
}
}
前驱节点:对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的前一个节点为该节点的前驱节点。
后继节点:对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的后一个节点为该节点的后继节点
/// <summary>
/// 前驱节点
/// 对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的前一个节点为该节点的前驱节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public Node Predecessor(Node node)
{
Node n = null;
if (node == null)
return null;
//1.如果有左子树,那么前驱节点是左子树中最大值
else if (HasLeft(node))
return Maximum(node.L);
//2.1如果是右孩子,node的前驱节点为它的父节点
else if (IsRight(node))
return node.Parent;
//2.2如果是左孩子则找到最低的父节点且父节点的右孩子是node所在的子树
//另一种说法:如果是左孩子则找到node所在子树被称为右子树的父节点
else if (IsLeft(node))
{
n = node.Parent;
while (n != null)
{
if (IsLeft(n))
n = n.Parent;
else
return n.Parent;
}
}
return null;
}
/// <summary>
/// 后继节点
/// 对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的后一个节点为该节点的后继节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public Node Successor(Node node)
{
Node n = null;
//1.如果有右子树,那么后继节点是左子树中最大值
if (HasRight(node))
return Minimum(node.R);
//2.1 如果是左孩子,node的后继节点为它的父节点
else if (IsLeft(node))
return node.Parent;
//2.2 如果是右孩子则找到最低的父节点且父节点的左孩子是node所在的子树
//另一种说法:如果是右孩子则找到node所在子树被称为左子树的父节点
else if (IsRight(node))
{
n = node.Parent;
while (n != null)
{
if (IsRight(n))
n = n.Parent;
else
return n.Parent;
}
}
return null;
}
/// <summary>
/// 查找
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Node Search(int key)
{
return Search(Root, key);
}
public Node Search(Node node,int key)
{
if(node == null|| node.Key==key)
{
return node;
}
if (node.Key > key)
return Search(node.L,key);
else
return Search(node.R, key);
}
/// <summary>
/// 插入
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Node Insert(int key)
{
return Insert(CreateNode(key));
}
public Node Insert(Node node)
{
if (Root == null)
{
Root = node;
return Root;
}
Node n = Root;
Node x =null;
while(n!=null)
{
x = n;
if (node > n)
n = n.R;
else if (node < n)
n = n.L;
else
{
n = n.L; //允许插入相同键值,如果不允许注释该行,将return注释取消
//return n;
}
}
node.Parent = x;
if (node > x)
x.R = node;
else
x.L = node;
return node;
}
删除节点存在 3 种情况,几乎所有类似博客都提到了这点。这 3 种情况分别如下:
/// <summary>
/// 删除
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public Node Remove(Node n)
{
Node x=null;
//1. 没有左右子节点
if (!HasChild(n))
{
if (IsLeft(n))
n.Parent.L = null;
else
n.Parent.R = null;
}
//2. 存在左节点或者右节点,删除后需要对子节点移动
else if (HasOneChild(n))
{
x = n.L == null ? n.R : n.L;
if (IsLeft(n))
n.Parent.L = x;
else
n.Parent.R = x;
}
//3. 同时存在左右子节点,通过和后继节点交换后转换为前两种情况(后继节点不可能存在左孩子,有可能有右孩子;)
else
{
//找到后继节点,将后继节点填到删除节点位置,(继节点不可能有左孩子,可能右孩子,也就是变为删除后继节点问题,且后继节点最多有一个孩子节点(右孩子))
Node successorNode = Successor(n); //找到删除节点n的后继节点,后继节点不可能存在左孩子,有可能有右孩子
Node rightNode = successorNode.R;
if(rightNode!=null)
{
rightNode.Parent = successorNode.Parent;
}
if (successorNode.Parent != null)
if (IsLeft(successorNode))
successorNode.Parent.L = rightNode;
else
successorNode.Parent.R = rightNode;
successorNode.Parent = n.Parent;
if (IsLeft(n))
n.Parent.L = successorNode;
else if (IsRight(n))
n.Parent.R = successorNode;
else
Root = successorNode;
successorNode.L = n.L;
if (n.L != null)
n.L.Parent = successorNode;
successorNode.R = n.R;
if (n.R != null)
n.R.Parent = successorNode;
}
n.L = null;
n.R = null;
n.Parent = null;
return n;
}
二叉搜索树可视化工具(旧金山大学 (usfca)|数据结构可视化工具)
文章浏览阅读3k次。原生html文件中引入vue.js的方法head标签下 通过script先引入vue.js文件<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>_原生项目中如何引入vue
文章浏览阅读3.6w次,点赞201次,收藏155次。问题描述起因是在使用requests包访问https请求时,出现了SSLError,提示Caused by SSLError(“Can’t connect to HTTPS URL because the SSL module is not available.”根据一些博客设置verify=False,依然无效。在尝试通过pip指定安装缺少的包时,也出现了类似的提示。通过pip install 命令安装包时,出现了Retrying (Retry(total=4, connect=None, _because the ssl module is not available
文章浏览阅读4.3k次。 简易成绩分析系统使用说明 四川省泸州市纳溪区大渡中学教务室制作使用一、特色1、限制条件少只需把原始成绩录入或粘贴入总表即可,对总表要求极为宽松:不必整理试卷、非顺序登分;各列(包括科目)名称、位置任意;不受班级、每班人数、科目等数量限制,行列不受限制。简言之,只要您原始成绩表是什么样,把它复制过来就行,只需注意本总表第一行为表头且有班级一列和不合并单元格_vba统计成绩代码及解析
文章浏览阅读2.7k次。一 查看全部设置项要查看完整的配置项,使用以下命名[root@mysqlserver ~]# mysqld --verbose --help二 各参数说明NameCmd-LineOption FileSystem VarVar ScopeDynamicaudit_log_buffer_sizeYesYesYesGlobalNoaudit_log_compressionYes_init_pool_size
文章浏览阅读8.1k次。cmake找不到Config.cmakeat /opt/ros/kinetic/share/catkin/cmake/catkinConfig.cmake:76 (find_package): Could not find a package configuration file provided by "ov_eval" with any of the following names:..._ecmconfig.cmake
文章浏览阅读144次。[ddms]Failed to initialize Monitor Thread: Unable to establish loopback connection,多半是防火墙引起2011-01-25 10:18[@@@ddms]Failed to initialize Monitor Thread: Unable to establish loopback connectio..._eclipse adb正常 ddms: failed to initialize monitor thread: unable to establi
文章浏览阅读2.1k次。golang 写循环执行的定时任务,常见的有以下三种实现方式:1、time.Sleep方法:for {time.Sleep(time.Second)fmt.Println("我在定时执行任务")}2、time.Tick函数:t1:=time.Tick(3*time.Second)for {select {case <-t1:..._time.tick
文章浏览阅读1.4k次,点赞3次,收藏9次。目录:前言一、UDP1.UDP协议格式2.UDP的特点3.应用场景:二、TCP1.TCP协议格式2.TCP内部机制3.TCP的特点4.应用场景三、TCP、UDP区别表四、总结前言网络数据的传输具体体现在每一个网络节点上一种规定的数据格式,不管是TCP还是UDP都是在传输层传输数据的一种规定的格式,也就是在传输过程中每一层的协议。UDP协议的报头构造简单,TCP 协议的构造复杂,在不同的场景下,两者各有各的特点一、UDP1.UDP协议格式由图可以看出:UDP的构造很简单,只有源端口和目的端口_udp与tcp协议的应用场景与特点?
文章浏览阅读6.4k次。在当前的世界中,数据和数据保护是企业至关重要的考虑因素。客户希望您确保其信息的安全性,如果您不能保证信息安全,就会失去业务。许多拥有敏感信息的客户在与您开展业务之前,会要求您部署坚固的数据安全基础架构。基于这种考虑,您是否相信自己企业内的IT安全性?_风险和漏洞
文章浏览阅读482次,点赞6次,收藏3次。1.中国最大的健康体检与健康管理机构——慈铭健康体检连锁机构(原慈济)的掌门人是()。3.据相关报道显示,家住在高速路附近的孕妇,生出来的孩子产生autism的比例较高。1.在《浪潮之巅》这本书中写到,太阳这个公司做到最大的营业额,一年做到()美金。1.医改不是卫生部一家的事情,而涉及到了很多部门,医改涉及到的以下部门是()。1.沈思曾经因为公司的产品被IOS下架,在苹果总部等了()个小时的相关负责人。2.在《浪潮之巅》这本书中写到,太阳这个公司是一个最高市值()亿美金的公司。”出自古代哪部著作?_创业创新领导力慕课几章
文章浏览阅读2.2k次。maven依赖:<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId>..._outworkbook.write(outputfilestream)
文章浏览阅读294次。FreeRtos基于STM32CUBEMX的103系列电灯操作_stm32 freertos 点灯