二叉查找树(二叉排序树、二叉搜索树)详解(可视化工具)_二叉树可视化工具-程序员宅基地

技术标签: 二叉排序树  二叉树  二叉搜索树  数据结构与算法  二叉查找树  数据结构  

二叉查找树又叫 二叉排序树二叉搜索树
文章中树的概念和二叉树的定义转自二叉查找树(一)之 图文解析 和 C语言的实现
前驱节点和后继节点 参考:二叉搜索树的前驱节点和后继节点
删除节点参考:二叉查找树 - 删除节点 详解(Java实现)
本文对前驱节点、后继节点、删除操作进行着重讲解
完整代码是C#实现
二叉树.图A

0X01 节点的定义

  • 节点的定义
    public class Node
    {
    
        public int Key;
        public Node Parent;//parent
        public Node L; //left
        public Node R; //right
    }
  • 树的定义
    public class BSTree
    {
    
        //树的根节点
        public Node Root;
    }

0X02 遍历

这里列举 前序遍历、中序遍历、后序遍历、层次遍历、Z形(蛇形)遍历
二叉搜索树的中序遍历是单调递增的

2.1 前序遍历

        public void Preorder(Node n)
        {
    
            if (n == null)
                return;
            Print(n);
            Preorder(n.L);
            Preorder(n.R);
        }

2.2 中序遍历

        /// <summary>
        /// 中序遍历
        /// </summary>
        /// <param name="n"></param>
        public void Inorder(Node n)
        {
    
            if (n == null)
                return;
            Inorder(n.L);
            Print(n);
            Inorder(n.R);
        }

二叉搜索树的中序遍历是单调递增的

2.3 后序遍历

        /// <summary>
        /// 后序遍历
        /// </summary>
        /// <param name="n"></param>
        public void Postorder(Node n)
        {
    
            if (n == null)
                return;
            Postorder(n.L);
            Postorder(n.R);
            Print(n);
        }

2.4 层次遍历

        /// <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);
            }
        }

2.5 Z形(蛇形)遍历

        /// <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);
                }
            }
        }

0X03 前驱节点和后继节点详解

前驱节点:对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的前一个节点为该节点的前驱节点。

后继节点:对一棵二叉树进行中序遍历,按照遍历后的顺序,当前节点的后一个节点为该节点的后继节点

3.1 前驱节点

在这里插入图片描述

  1. 若一个节点有左子树,那么该节点的前驱节点是其左子树中键值最大的节点(按照中序遍历,左子树中最大的节点后继就是父节点即当前节点)(果有左子树,那么前驱节点是左子树中最大值
  2. 若一个节点没有左子树,那么判断该节点和其父节点的关系 :
    2.1 若该节点是其父节点的右边孩子,那么该节点的前驱结点即为其父节点。(若该节点是右孩子,则前驱节点为它的父节点)(按照中序遍历该节点是右孩子且没有该节点没有左孩子,则该节点的父节点的右子树中最小的节点即是后续节点,反过来父节点为该节点的前驱节点)
    例:上图7的前驱是6
    2.2 若该节点是其父节点的左边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的右边孩子,那么Q就是该节点的后继节点。(若该节点是左孩子则找到最近的父节点且父节点的右孩子是该节点所在的子树,找到的那个父节点即为前驱节点
    例:上图2的前驱是1,8的前驱是7
        /// <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;
        }

3.2 后继节点

在这里插入图片描述

  1. 若一个节点有右子树,那么该节点的后继节点是其右子树中val值最小的节点(如果有右子树,那么后继节点是左子树中最大值
  2. 若一个节点没有右子树,那么判断该节点和其父节点的关系 :
    2.1 若该节点是其父节点的左边孩子,那么该节点的后继结点即为其父节点 (如果是左孩子,该节点的后继节点为它的父节点
    例:上图8的后继是9
    2.2 若该节点是其父节点的右边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的左边孩子,那么Q就是该节点的后继节点(该节点如果是右孩子则找到最近的父节点且父节点的左孩子是node所在的子树,找到的那个父节点即为后继节点
    例:上图4的后继是5
        /// <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;
        }

0X04 查找

        /// <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);
        }

0X05 插入

        /// <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;
        }

0X06 删除详解

在这里插入图片描述
在这里插入图片描述

删除节点存在 3 种情况,几乎所有类似博客都提到了这点。这 3 种情况分别如下:

  1. 没有左右子节点,可以直接删除
  2. 存在左节点或者右节点,删除后需要对子节点移动
  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;
        }

0X07 完整代码(C#)

完整代码(github)

0X08 可视化工具

二叉搜索树可视化工具(旧金山大学 (usfca)|数据结构可视化工具)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/codingriver/article/details/107548386

智能推荐

前端原生html引入vue.js_原生项目中如何引入vue-程序员宅基地

文章浏览阅读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

Can‘t connect to HTTPS URL because the SSL module is not available - 关于anaconda中的SSL模块错误-程序员宅基地

文章浏览阅读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

我用EXCEL VBA制作了一个成绩管理系统,请各位多多指导!_vba统计成绩代码及解析-程序员宅基地

文章浏览阅读4.3k次。 简易成绩分析系统使用说明 四川省泸州市纳溪区大渡中学教务室制作使用一、特色1、限制条件少只需把原始成绩录入或粘贴入总表即可,对总表要求极为宽松:不必整理试卷、非顺序登分;各列(包括科目)名称、位置任意;不受班级、每班人数、科目等数量限制,行列不受限制。简言之,只要您原始成绩表是什么样,把它复制过来就行,只需注意本总表第一行为表头且有班级一列和不合并单元格_vba统计成绩代码及解析

Mysql配置参数详解(一)_init_pool_size-程序员宅基地

文章浏览阅读2.7k次。一 查看全部设置项要查看完整的配置项,使用以下命名[root@mysqlserver ~]# mysqld --verbose --help二 各参数说明NameCmd-LineOption FileSystem VarVar ScopeDynamicaudit_log_buffer_sizeYesYesYesGlobalNoaudit_log_compressionYes_init_pool_size

cmake找不到XXXConfig.cmake文件_ecmconfig.cmake-程序员宅基地

文章浏览阅读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

安装了ADT 插件的Eclipse 启动时会报Failed to initialize Monitor Thread 这样的错误-程序员宅基地

文章浏览阅读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

随便推点

Golang中定时任务time.Sleep和time.Tick的优劣对比-程序员宅基地

文章浏览阅读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

UDP和TCP内部机制、特点、区别、应用场景——超详细(配图)!_udp与tcp协议的应用场景与特点?-程序员宅基地

文章浏览阅读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协议的应用场景与特点?

IT安全漏洞、威胁与风险的区别,你都知道吗?_风险和漏洞-程序员宅基地

文章浏览阅读6.4k次。在当前的世界中,数据和数据保护是企业至关重要的考虑因素。客户希望您确保其信息的安全性,如果您不能保证信息安全,就会失去业务。许多拥有敏感信息的客户在与您开展业务之前,会要求您部署坚固的数据安全基础架构。基于这种考虑,您是否相信自己企业内的IT安全性?_风险和漏洞

《创新创业领导力》_创业创新领导力慕课几章-程序员宅基地

文章浏览阅读482次,点赞6次,收藏3次。1.中国最大的健康体检与健康管理机构——慈铭健康体检连锁机构(原慈济)的掌门人是()。3.据相关报道显示,家住在高速路附近的孕妇,生出来的孩子产生autism的比例较高。1.在《浪潮之巅》这本书中写到,太阳这个公司做到最大的营业额,一年做到()美金。1.医改不是卫生部一家的事情,而涉及到了很多部门,医改涉及到的以下部门是()。1.沈思曾经因为公司的产品被IOS下架,在苹果总部等了()个小时的相关负责人。2.在《浪潮之巅》这本书中写到,太阳这个公司是一个最高市值()亿美金的公司。”出自古代哪部著作?_创业创新领导力慕课几章

java——java使用poi操作excel,java excel读写_outworkbook.write(outputfilestream)-程序员宅基地

文章浏览阅读2.2k次。maven依赖:<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId>..._outworkbook.write(outputfilestream)

stm32CubeMX的FreeRtos基于103系列的电灯入门_stm32 freertos 点灯-程序员宅基地

文章浏览阅读294次。FreeRtos基于STM32CUBEMX的103系列电灯操作_stm32 freertos 点灯

推荐文章

热门文章

相关标签