树与二叉树深度剖析(二)

news2024/11/16 3:24:46

一. 树表示法

1.双亲表示法

(1).含义

 在一棵树中,任意一个结点的双亲只有一个,这是由树的定义决定的。双亲表示法就是利用了树的这个性质,在存储结点信息的同时,在每个节点中附设一个指向其双亲的指针, 指向双亲在链表中的位置。这种结构一般借助数组来实现。这样的链表也称为静态链表。

(2).实现思路

 在双亲链表表示法中,根节点没有双亲,其parent指向-1,其余结点的parent指针为存放其双亲结点的数组下标值。双亲表示法简单,易懂,易实现,求指定结点的双亲和祖先非常方便,但是要求某个结点的孩子和兄弟,需要遍历整个数组。

2.孩子表示法

(1).含义

 树的每个节点都有自己的孩子,孩子表示法是指在树的每个节点中设置指针指向该节点的孩子。由于一般树中的结点可能存在多个孩子,因此需要链表依次存储结点的所有孩子。孩子链表的存储结构需要同时使用数组和单链表实现。

(2).实现

 孩子链表最左边是结点在数组中的索引,中间一列表示结点的数据,最后一列是指向孩子链表的指针,孩子链表使用单链表实现,里面存放的并不是结点本身,而是结点在数组中的索引。与双亲链表相反,孩子链表表示法便于实现涉及孩子及子孙的操作,但不利实现与双亲有关的操作。

3. 双亲孩子链表表示法

(1).含义

 树的孩子双亲表示法结构如图,它增加了一个列用于存放结点的双亲在数组中的索引。双亲孩子链表表示法在实际操作中,无论是查找节点的孩子,还是双亲或是遍历整棵树都很容易实现。

4.孩子兄弟表示法

(1).含义

 孩子兄弟表示法是一种二叉树表示方法,即用二叉链表作为树的存储结构。与二叉树的二叉链表表示所不同的是,这里的二叉链表节点指针不在去指向左、右孩子,而是指向该结点的第一个孩子(FirstChild)和下一个兄弟节点(NextSibling)

二. 森林

 孩子兄弟链表表示法来存储树,实际上使用二叉链表的形式来存储的。而森林是树的有限集合,他也可以用二叉树来表示。可见,树,二叉树,森林之间存在着确定的关系,而且可以相互转换。

1.一般树转为二叉树

(1). 连线:在所有兄弟节点之间加一条连线

(2). 切线:对于每个结点,除了保留与其最左孩子的连线外,去掉该结点与其他孩子的连线。

(3). 旋转:将所有水平方向的连线顺时针旋转45度

2. 森林转为二叉树

 森林是树的集合,把森林转换为二叉树的方法是:现将森林中每一棵树转换为二叉树,然后将二叉树的根节点作为兄弟连在一起。

3. 二叉树还原为一般树

 如果一棵二叉树可以还原为一般树,那么这个二叉树肯定没有右子树,其还原过程也分三个步骤:

(1). 连线:如果某结点N是双亲节点的左孩子,则将该结点N的右孩子及其沿着其右链不断搜索到右孩子,都分别与结点N的双亲结点用虚线连接。

(2). 切线:去掉原二叉树中每个结点与其右孩子之间的连线,仅保留与其左孩子之间的连线。

(3). 把虚线改为实线,按照层次整理好。

4. 二叉树还原成森林

 (1). 将二叉树的根节点与沿着其右链不断搜索得到的所有右孩子的连线全部抹去,这样就得到包含若干棵二叉树的森林。

 (2). 每棵二叉树还原为一般树,这样就可以得到森林。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

三. 森林遍历

1. 含义

 以某种方式访问树中的每一个结点,且仅访问一次。

2. 分类

 (1).先根遍历:若树非空,则先访问根结点,再按照从左到右的顺序遍历根结点的每一棵子树。这个访问顺序与这棵树对应的二叉树的【先序遍历】顺序相同。

 (2).后根遍历:若树非空,则按照从左到右的顺序遍历根结点的每一棵子树,之后再访问根结点。其访问顺序与这棵树对应的二叉树的【中序遍历】顺序相同。

案例:

  森林的先根遍历:A-B-C-D-E-F-G-H-J-I

二叉树森林的先序遍历:A-B-C-D-E-F-G-H-J-I(相同)

完整二叉树的先序遍历:A-B-C-D-E-F-G-H-J-I (相同)

森林的后根遍历:B-C-D-A-F-E-J-H-I-G

二叉树森林的后序遍历:D-C-B-A-F-E-J-I-H-G

完整二叉树的后序遍历:D-C-B-F-J-I-H-G-E-A(不同于二叉树森林的后序遍历)

二叉树森林的中序遍历:B-C-D-A-F-E-J-H-I-G(与森林的后根遍历相同)

完整二叉树的中序遍历:B-C-D-A-F-E-J-H-I-G(与森林的后根遍历相同,自然也与二叉树森林的中序遍历相同

四. 二叉树求解四则运算

1. 目标

 将四则运算 3+2*9-16/4 转换成二叉树的表现形式。

PS:这里我们在设计算法的时候只考虑最简单的四则运算,不考虑括号,开方,求余等。

2. 特点

 操作数字都是叶子结点, 运算符都是内部结点,优先运算符都在下面。

3. 转换过程

(1). 解析获取表达式的第一个字符3,因为表达式的树为空树,所以3是根节点。

(2). 获取第二个字符+,此时根结点为数字,将新结点作为根节点,原结点作为新结点的左孩子。

只有第二个会有这个可能,以后根节点肯定只为操作符。

(3). 获取第二个字符2,数字将沿着根结点插入到最右端。

(4). 获取第二个结点*,如果是操作符同根节点比较优先级,如果新结点的优先级高,则成为根结点的右孩子,原根结点的右孩子成为新结点的左子树。

(5). 获取第五个字符9,数字将沿着根节点直接插入到最右端。

(6). 获取第六个字符-,-与根结点+比较优先级,优先级相等,新结点成为根结点,原表达式成为新节点的左子树。

(7). 获取第七、八个字符组合为数字16,沿着根结点的右链直接插入到最右端。

(8). 获取第9个字符 / ,与根结点比较优先级,优先级高,成为根结点的右孩子,原根结点的右孩子成为左子树。

(9). 获取第10个字符4,数字沿根结点右链直接插入到最右端

4. 总结

(1). 第一个节点先成为表示树的根

(2). 第二个结点插入时变为根,原根结点变为新结点的左孩子。

(3). 插入节点为数字时,沿根结点右链插入到最右端。

(4). 插入节点为操作符时,先与根结点操作符优先级对比。

a.优先级不高时,新结点成为根结点,原表达式成为新结点的左子树。

b.优先级高时,新结点成为根结点的右孩子,原根结点的右孩子成为新结点的左子树。

代码分享:

节点类:

  /// <summary>
    /// 树节点类
    /// </summary>
    public class Node
    {
        public Node Left { get; set; }  //左孩子

        public Node Right { get; set; }  //右孩子

        public int Data { get; set; } //数据

        public bool IsOptr { get; }    //是否为操作符

        /// <summary>
        /// 数据构造
        /// </summary>
        /// <param name="data"></param>
        public Node(int data)
        {
            this.Data = data;
            this.IsOptr = false;
        }

        /// <summary>
        /// 操作符构造
        /// </summary>
        /// <param name="data"></param>
        public Node(char data)
        {
            this.Data = data;
            this.IsOptr = true;
        }

        public override string ToString()
        {
            if (IsOptr)
            {
                return Convert.ToString((char)Data);
            }
            else
            {
                return Data.ToString();
            }
        }



    }

二叉树实现类:

 /// <summary>
    /// 二叉树类处理四则运算
    /// </summary>
   public class BinaryTree
    {
        //成员变量
        private Node _head;     //头指针
        private string expStr;      //表达式字符串
        private int pos = 0;        //解析字符串当前位置

        public BinaryTree(string constructStr)
        {
            this.expStr = constructStr;
            CreateTree();
        }
        //创建表达式的树
        public void CreateTree()
        {
            while (pos < expStr.Length)
            {
                Node node = GetNode();
                if (_head == null)
                {
                    //根节点不存在,第一个结点就为根
                    _head = node;
                }
                else if (!_head.IsOptr)
                {
                    //若根节点为数字,当前节点为根,之前的根变为左孩子
                    node.Left = _head;
                    _head = node;
                }
                else if (!node.IsOptr)
                {
                    //当前节点为数字
                    Node tempNode = _head;
                    while (tempNode.Right != null)
                    {
                        tempNode = tempNode.Right;
                    }
                    tempNode.Right = node;
                }
                else
                {
                    if (GetPriority((char)node.Data) <= GetPriority((char)_head.Data))
                    {
                        //优先级不高时,新结点成为根结点,原表达式成为新结点的左子树
                        node.Left = _head;
                        _head = node;
                    }
                    else
                    {
                        //优先级高时,新结点成为根结点的右孩子,原根结点的右孩子成为新结点的左子树
                        node.Left = _head.Right;
                        _head.Right = node;
                    }
                }
            }
        }
        //创建节点
        private Node GetNode()
        {
            char ch = expStr[pos];      //获取当前解析的字符
            if (char.IsDigit(ch))        //判断当前字符是否为数字
            {
                //若操作的数字大于1位,需要用循环获取
                StringBuilder numStr = new StringBuilder();
                while (pos < expStr.Length && char.IsDigit(ch = expStr[pos]))
                {
                    pos++;
                    numStr.Append(ch);
                }
                return new Node(Convert.ToInt32(numStr.ToString()));
            }
            else
            {      //为操作符
                pos++;
                return new Node(ch);
            }
        }
        //获取运算符的优先级,乘除优先级要高于加减
        private int GetPriority(char optr)
        {
            if (optr == '+' || optr == '-')
            {
                return 1;
            }
            else if (optr == '*' || optr == '/')
            {
                return 2;
            }
            else
            {
                return 0;
            }
        }
        private int PreOrderCalc(Node node)
        {
            int n1, n2;
            if (node.IsOptr)
            {
                //先遍历计算表达式的结果
                n1 = PreOrderCalc(node.Left);
                n2 = PreOrderCalc(node.Right);
                char optr = (char)node.Data;
                switch (optr)
                {
                    case '+':
                        node.Data = n1 + n2;
                        break;
                    case '-':
                        node.Data = n1 - n2;
                        break;
                    case '*':
                        node.Data = n1 * n2;
                        break;
                    case '/':
                        node.Data = n1 / n2;
                        break;
                }
            }
            return  node.Data  ;
        }
        //获取四则运算的值
        public int GetResult()
        {
            return PreOrderCalc(_head);
        }
    }

测试

            Console.WriteLine("-------------------二叉树处理四则运算-------------------------");

            string expStr ="3+2*9-16/4";
            //创建二叉树
            BinaryTree bTree = new BinaryTree(expStr);
            Console.WriteLine($"{expStr}结果为:{ bTree.GetResult()} ");

            Console.ReadKey();

运行结果:

原文链接:第十节:树与二叉树深度剖析(二) - Yaopengfei - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IMX Linux 用户手册 — 3

IMX Linux 用户手册 — 3 10.3 CAAM Job Ring后端驱动规范 CAAM作业环后端驱动(caam_jr)实现并使用作业环接口(JRI)向CAAM引擎提交来自前端驱动(caamalg, caamhash, caampkc, caamrng, caamkeyblob)的加密API服务请求。 CAAM驱动程序有几个选项&#xff0c;最显著的是硬件工作…

ssm宿舍水电费报修考勤管理系统的开发与实现

目 录 摘 要 I 目 录 1 第1章 概述 1 1.1 研究背景 1 1.2 研究现状 1 1.3 研究内容 2 第二章 开发技术介绍 2 2.1 系统开发平台 2 2.2 平台开发相关技术 3 2.2.1 B/S架构 3 2.2.2 mysql数据库介绍 4 2.2.3 JSP技术介绍 4 第三章 系…

如何增强企业数字敏捷性?|身份云研究院

在经济下行背景中&#xff0c;企业面临数字化转型和降本增效两大课题&#xff0c;解决这两个问题的核心是增强企业数字敏捷性。比起从业务侧逐个优化&#xff0c;决策者们更应从数字化基础设施入手&#xff0c;搭好底层数字化架构来保障企业在不断变化的技术和法规中获得适应性…

随时随地查看远程试验数据与记录——IPEhub2与IPEmotion APP

一 背景 在工况恶劣、空间狭小的试验场景或工程机械领域中&#xff0c;不但试验人员在试验环境中对自身安全没有保障&#xff0c;而且试验过程也会受到影响&#xff0c;如高温高压测试、工程机械液压系统测试等。对此&#xff0c;结合IPEhub2与IPEmotion APP&#xff0c;既可保…

linux journalctl使用详解

1.概述 ​ journalctl 用来查询 systemd-journald 服务收集到的日志。systemd-journald 服务是 systemd init 系统提供的收集系统日志的服务。journalctl通常用来查询systemd管理的Unit的日志信息。 2.使用方法 $ man journalctl $ journalctl --help 常用方法 $ journalctl …

【可解释性机器学习】解释基于Scikit-learn进行文本分类的pipeline及结果

使用Scikit-learn进行文本分类pipeline1. 基线模型2. 基线模型&#xff0c;改进的数据3. Pipeline改进4. 基于字符的pipeline5. 调试HashingVectorizer参考资料scikit-learn文档提供了一个很好的文本分类教程。确保先阅读它。 本文中&#xff0c;我们将做类似的事情&#xff0c…

学习笔记-----通道

加粗样式# system v共享内存 进程通信的前提条件是&#xff1a;让不同进程看见同一份资源。 共享内存&#xff1a;其实就是进程获取共享区里面的地址&#xff0c;该地址为物理内存中某块我所需要资源的地址(该内存是创建的共享内存处在共享区里)&#xff0c;地址通过页表映射到…

项目工时管理遇难题?看看这套工时管理系统解决方案

随着社会化大生产的发展以及市场竞争的日趋激烈&#xff0c;现代企业的规模在不断扩大。对于项目企业来说&#xff0c;人力资源的成本就是项目的主要成本&#xff0c;而工时是项目中人工成本的重要依据&#xff0c;因此&#xff0c;管理好员工工时是项目管理过程中最重要的任务…

计算机图形学 第3章 圆的扫描转换-第三章结束

书用的是 书名:计算机图形学基础教程&#xff08;VisualC版&#xff09;&#xff08;第二版&#xff09; 定价&#xff1a;44.5元 作者:孔令德 出版社&#xff1a;清华大学出版社 出版日期&#xff1a;2013-03-01 ISBN&#xff1a;9787302297529 目录习题3&#xff08;续&#…

【数据结构】8.3 交换排序

文章目录1. 冒泡排序冒泡排序算法冒泡排序算法分析2. 快速排序快速排序算法快速排序算法分析基本思想 每两个元素之间互相比较&#xff0c;如果发现大小关系相反&#xff0c;则将他们交换过来&#xff0c;直到所有记录都排好序为止。假设希望是从小到大来排序&#xff0c;结果…

Nginx-反向代理配置学习总结

Nginx-反向代理配置学习总结 正向代理&#xff1a;指的是通过代理服务器 代理浏览器/客户端去重定向请求访问到目标服务器 的一种代理服务&#xff0c;正向代理服务的特点是代理服务器 代理的对象是浏览器/客户端&#xff0c;也就是对于目标服务器 来说浏览器/客户端是隐藏的。…

文件的IO

一、文件的定义狭隘的文件:指你的硬盘上的文件和目录.广义的文件:泛指计算机中的硬件资源,操作系统中,把很多硬件设备和软件资源都抽象成了文件,按照文件的形式统一管理.比如网卡,操作系统也是把网卡抽象成了文件资源,所以说操作网卡其实和操作文件的方式是基本一样的.而我们本…

初识流计算框架Spark

Spark简介 Spark最初由美国加州伯克利大学&#xff08;UCBerkeley&#xff09;的AMP&#xff08;Algorithms, Machines and People&#xff09;实验室于2009年开发&#xff0c;是基于内存计算的大数据并行计算框架&#xff0c;可用于构建大型的、低延迟的数据分析应用程序。Sp…

一刷代码随想录——链表

1.理论基础链表节点的定义&#xff1a;struct ListNode {int val;ListNode* next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode* next) : val(x), next(next) {} };根据卡哥提示&#xff0c;由于力扣中已经给出如…

C++中拷贝构造函数、拷贝赋值运算符、析构函数、移动构造函数、移动赋值运算符(三/五法则)

1、介绍 三五法则是针对C中类的成员和类对象的操作函数。 三法则是指&#xff1a;拷贝构造函数、拷贝赋值运算符、析构函数。 五法则是在三法则的基础上增加了&#xff1a;移动构造函数、移动赋值运算符。 2、拷贝构造函数 定义&#xff1a;如果构造函数的第一个参数是自身…

Postman前置脚本

位置&#xff1a;作用&#xff1a;调用脚本之前需要执行的代码片段一、产生随机数字生成0-1之间的随机数&#xff0c;包括0&#xff0c;不包括1&#xff1b;var random Math.random();console.log("随机数",random);获取最小值到最大值之前的整数随机数function Get…

2019-ICML-Graph U-Nets

2019-ICML-Graph U-Nets Paper: https://arxiv.org/abs/1905.05178 Code: https://github.com/HongyangGao/Graph-U-Nets 图U-Nets 作者将CNN上的U-Net运用到了图分类上&#xff0c;因为我们主题是图分类&#xff0c;就不对U-Net进行论述了&#xff0c;只对其中的gPool&#…

eureka 读写锁的一点思考

读写锁 读写锁一般实现 读读不互斥 读写互斥 写写互斥 读写锁的好处是&#xff0c;面对读多写多的场景会拥有比较好的表现 一般我们会在读操作加上读锁&#xff0c;写操作加上写锁。但是最近我发现eureka 在使用读写锁的时候是相反的&#xff0c; 也就是说在读操作加上了读锁&…

2023最值得入手的运动耳机是哪款、口碑最好的运动蓝牙耳机推荐

不知道有没有和我一样的小伙伴&#xff0c;在运动时特别喜欢听音乐&#xff0c;每次听到一首合适的音乐&#xff0c;感觉运动起来都更有激情和活力了。所以这时候就需要挑选一款舒适的耳机了。别看市面上各种各样的运动耳机很多&#xff0c;但实际上能真正适合运动的少之又少&a…

oss服务端签名后直传分析与代码实现

文章目录1.简介1.1 普通上传方式1.2 服务端签名后直传3.服务端签名后直传文档3.1 用户向应用服务器请求上传Policy和回调。3.2 应用服务器返回上传Policy和签名给用户。3.3 用户使用Post方法向OSS发送文件上传请求。4.实战开发-后端4.1 pom.xml核心配置4.2 application.yml核心…