【数据结构与算法】二叉树的运用要点

目录

 一,二叉树的结构深入认识

 二,二叉树的遍历

 三,二叉树的基本运算

3-1,计算二叉树的大小

3-2,统计二叉树叶子结点个数

3-3,计算第k层的节点个数

3-4,查找指定值的结点


一,二叉树的结构深入认识

        二叉树是不可随机访问的,二叉树的结构是通过双亲结点与孩子结点之间的连接进行遍历访问,因此,二叉树的结构是用链式结构来存储的。如下:

二叉树的结构

#include <stdio.h>
#include <stdlib.h>
typedef struct Tree {
    int val;//数据
    struct Tree* leftchild;//左孩子结点
    struct Tree* rightchild;//右孩子结点
}Tree;

        要说明的是,学习二叉树的结构不跟栈和队列之类的一样用于增删查改,二叉树没有这些操作,二叉树的运用比较复杂,下面会依次讲解。


 二,二叉树的遍历

        二叉树的遍历有:前序遍历,中序遍历,后序遍历,层序。通常,在这些遍历算法中除了层序遍历外,其它的遍历都要运用递归结构。

        1,前序遍历(也称先序遍历)——访问根结点的操作发生在遍历其左右子树之前,即遍历的

顺序为:根,左子树,右子树。

        2,中序遍历——访问根结点的操作发生在遍历其左右子树之间。即遍历的顺序为:左子树,根,右子树。

        3,后序遍历——访问根结点的操作发生在遍历其左右子树之后。即遍历的顺序为:左子树,右子树,根。

        4,层序遍历——从左到右一层一层的遍历,此遍历非常简单,就如从开始到结尾的遍历顺序表一样。

        注意:这里要说明的是,以上的遍历除了层序遍历外,其它的遍历都是递归式的遍历,可不是单纯普通式按照以上顺序循环式的遍历,正确的遍历如下图:

        说明一下,在二叉树的遍历中,前,中,后遍历的思路基本相同,而层序遍历一般要借助队的结构来实现,本篇文章先不做介绍,后文深入运用时会详细说明。前中后的三种遍历代码如下:

//前序遍历
void FrontOrder(struct Tree* root) {//当递归到空结点时退出if (root == NULL) {return;}//输出fprintf(stdout, "%d ", root->val);//递归左子树FrontOrder(root->leftchild);//递归右子树FrontOrder(root->rightchild);
}
//中序遍历
void MiddleOrder(struct Tree* root) {//当递归到空结点时退出if (root == NULL) {return;}//递归左子树MiddleOrder(root->leftchild);//输出fprintf(stdout, "%d ", root->val);//递归右子树MiddleOrder(root->rightchild);
}
//后序遍历
void RearOrder(struct Tree* root) {//当递归到空结点时退出if (root == NULL) {return;}//递归左子树RearOrder(root->val);//递归右子树RearOrder(root->val);//输出fprintf(stdout, "%d ", root->val);
}

解析:

        递归本身有点不好理解,而上面的递归遍历中,当进行递归遍历时,可看做从此函数的根结点开始不断往下面的左右孩子结点遍历,当递归到根结点为空时结束下一次递归,并返回到此结点的双亲结点,可以说二叉树的递归遍历是不断往下层进行的,只有当遍历到最下层或下层的空结点时才返回,也就是递归遍历是从最后开始,然后不断往回返,直到返回到二叉结构的根节点才结束整个递归程序。

补充:

        函数的递归其实跟我们学习的栈结构一样,递归入函数(即进入函数栈帧)相当于入栈,出函数(即函数栈帧的销毁)相当于出栈。

学习建议:

        遍历思想是学习二叉树的基本,很多二叉树的算法思想都要在此递归遍历的思想上进行开阔的,因此,如果这中递归思路不明白,一定要先理清思路,不建议先往后面看。


三,二叉树的基本运算

        在讲解这一部分内容前,我们要先明白递归中设置局部变量的用法。

二叉树递归设置局部变量的注意:

        在二叉树计数中,我们难免要设定局部变量,而在进行递归返回中可能有些人说函数的返回值会覆盖之前的值,不明白为什么覆盖后的值就是我们想要统计的数值。其实这原理很简单,在讲解二叉树遍历的解析说过,二叉树的递归遍历是不断往下层进行的,只有当遍历到最下层时才返回,也就是递归程序是从最后开始,不断往前返回,当我们递归遍历二叉树时,满足计数的条件会不断往上层返,这时计数的值相当于以此函数中root为根结点的子树的以下计数值,也就是我们要统计的数值。

3-1,计算二叉树的大小

        计算二叉树的大小说白了就是确定有几个不为空的结点,此算法比较简单,我们可直接遍历整个二叉结构不断加一来实现。代码如下:

//统计二叉树中叶子结点的个数
int TreeSize(Tree* root) {//当为空结点时说明此时为0if (root == NULL) {return 0;}//不断递归遍历,遍历一个结点加1。return TreeSize(root->leftchild) + TreeSize(root->rightchild) + 1;
}

        以上代码虽然省力,但可能对于部分人来说比较难理解,展开后的代码如下:

int TreeSize(Tree* root) {if (root == NULL) {return 0;}//遍历左子树的结点个数int leftsize = TreeSize(root->leftchild);//遍历右子树的结点个数int rightsize = TreeSize(root->rightchild);//返回以此结点为根结点的二叉树结点个数,此二叉树是子二叉树return leftsize + rightsize + 1;
}

        对于当初学者笔者建议用展开后的代码,对递归了解比较深入后再用合成版代码。

3-2,统计二叉树叶子结点个数

        此算法与计算二叉树的结点个数方法相似,不同的是,当进行递归遍历时,我们可利用叶子结点的左右孩子都为空的特点来计数,当递归遍历时满足这一特点进行计数,不满足进行递归遍历,代码如下:

int LeavesNodeSize(Tree* root) {if (root == NULL) {return 0;}//当此结点是叶子结点时,计数1if (root->leftchild == NULL && root->rightchild == NULL) {return 1;}//左孩子结点的叶子结点数int count1 = LeavesNodeSize(root->leftchild);//右孩子结点的叶子结点数int count2 = LeavesNodeSize(root->rightchild);//返回当前以root为根结点的子二叉树的叶子结点总个数return count1 + count2;
}

        当我们明白以上原理后可进行简化算法,跟之前一样,先理清以上思路再进行简化。如下:

int LeavesNodeSize(Tree* root) {if (root == NULL) {return 0;}if (root->leftchild == NULL && root->rightchild == NULL) {return 1;}return LeavesNodeSize(root->leftchild) + LeavesNodeSize(root->rightchild);
}
3-3,计算第k层的节点个数

        记录第k层的二叉树结点也是同理,在递归遍历过程中,往下层递归一次将k减1,当k==1时就递归到了第k层,也就是在此时开始计数,而函数返回值将返回以此函数中的root为根结点的子二叉树的第k层结点数。

int NodeCount(Tree* root, int k) {if (root == NULL) {return 0;}//当k == 1 时此时遍历到了第k层,此时计数if (k == 1) {return 1;}//以此函数的root为根结点以下的子二叉树第k层的左孩子结点数int leftchild = NodeCount(root->leftchild, k - 1);//以此函数的root为根结点以下的子二叉树第k层的右孩子结点数int rightchild = NodeCount(root->rightchild, k - 1);//返回以此函数的root为根结点以下的子二叉树第k层的结点数return leftchild + rightchild;
}

        算法合并简化后如下:

int NodeCount(Tree* root, int k) {if (root == NULL) {return 0;}if (k == 1) {return 1;}return NodeCount(root->leftchild, k - 1) + NodeCount(root->rightchild, k - 1);
}
3-4,查找指定值的结点

        此算法的坑比较多,首先我们要考虑的是当遍历到要查找的结点时如何停止遍历,并最终返回该结点。要知道一点,当我们要查找的结点在中间时,直接返回是上一次的递归函数,因此,我们要做的是让查找的指定结点不断返回,直到递归结束。

        算法详细步骤如下:

Tree* FoundNode(Tree* root, int x) {if (root == NULL) {return NULL;}if (root->val == x) {return root;}Tree* leftchild = FoundNode(root->leftchild, x);//当左孩子是我们要找的结点时就不往下继续遍历了,直接返回此结点if (leftchild != NULL && leftchild->val == x) {return leftchild;}Tree* rightchild = FoundNode(root->rightchild, x);//当左孩子是我们要找的结点时就不往下继续遍历了,直接返回此结点if (rightchild != NULL && rightchild->val == x) {return rightchild;}//当查找不到返回NULLreturn NULL;
}

        算法的化简代码如下:

Tree* FoundNode(Tree* root, int x) {if (root == NULL) {return NULL;}if (root->val == x) {return root;}Tree* leftchild = FoundNode(root->leftchild, x);//当左孩子是我们要找的结点时就不往下继续遍历了,直接返回此结点if (leftchild != NULL && leftchild->val == x) {return leftchild;}//遍历右结点,当右孩子是我们要找的结点时返回此结点return FoundNode(root->rightchild, x);
}

总结:学习到这里学者们明显感到难度增大了,不过也不用担心,只要我们多多思考并加之练习,其实逻辑也不是那么难,上面的算法程序笔者之所以将步骤展开和步骤合并一并写入,就是为了让大家多多练习以上的思路,其实逻辑也都是一样的,只是为了强化思维罢了。

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

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

相关文章

Spring底层原理(二)

Spring底层原理(二) BeanFactory的实现 //创建BeanFactory对象 DefaultListableBeanFactory factory new DefaultListableBeanFactory(); //注册Bean定义对象 AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition(SpringConfig.class).set…

搭建Pytorch的GPU环境超详细

效果 1、下载和安装VS2019 https://visualstudio.microsoft.com/zh-hans/vs/older-downloads/ 登录需要用户名和密码 安装后需要联网下载组件的,安装的时候要勾选使用C++的桌面开发 2、下载和安装显卡驱动 查看自己的显卡型号 从英伟达下载和安装最新驱动

Python学习笔记——MYSQL,SQL核心

食用说明&#xff1a;本笔记适用于有一定编程基础的伙伴们。希望有助于各位&#xff01; SQL语言分类 SQL注释 库管理 表管理 数据操作 分组聚合 分页限制 需要注意的是关键字的顺序不可以错乱&#xff0c;否则会报错其中LIMIT关键字的n是指从第n个开始&#xff0c;m是指查…

【Zero to One系列】微服务Hystrix的熔断器集成

前期回顾&#xff1a; 【Zero to One系列】springcloud微服务集成nacos&#xff0c;形成分布式系统 【Zero to One系列】SpringCloud Gateway结合Nacos完成微服务的网关路由 1、hystrix依赖包 首先引入hystrix相关的依赖包&#xff0c;版本方面自己和项目内相对应即可&#…

Electron 学习

Electron基本简介 如果你可以建一个网站&#xff0c;你就可以建一个桌面应用程序。Eletron 是一个使用 JavaScript, HTML和 CSS等Web 技术创建原生程序的框架&#xff0c;它负责比较难搞的部分&#xff0c;你只需把精力放在你的应用的核心上即可。 Electron 可以让你使用纯 Jav…

kr第三阶段(二)32 位汇编

编译与链接 环境配置 masm32 masm32 是微软的 masm32 的民间工具集合。该工具集合除了 asm32 本身的汇编器 ml 外还提供了&#xff1a; SDK 对应的函数声明头文件和 lib 库。32 位版本的 link&#xff08;原版本是 16 位&#xff0c;这里的 32 位版本的 link 来自 VC 6.0&a…

OS 处理机调度

目录 处理机调度的层次 高级调度 作业 作业控制块 JCB 作业调度的主要任务 低级调度 中级调度 进程调度 进程调度时机 进程调度任务 进程调度机制 排队器 分派器 上下文切换器 进程调度方式 非抢占调度方式 抢占调度方式 调度算法 处理机调度算法的目标 处理…

Python桌面应用之XX学院水卡报表查询系统(Tkinter+cx_Oracle)

一、功能样式 Python桌面应用之XX学院水卡报表查询系统功能&#xff1a; 连接Oracle数据库&#xff0c;查询XX学院水卡操作总明细报表&#xff0c;汇总数据报表&#xff0c;个人明细报表&#xff0c;进行预览并且支持导出报表 1.总明细报表样式 2.汇总明细样式 3.个人明细…

安卓使用android studio跨进程通信之AIDL

我写这篇文章不想从最基础的介绍开始,我直接上步骤吧. 1.创建服务端 1.1:创建服务端项目:我的as版本比较高,页面就是这样的 1.2:创建AIDL文件,右键项目,选中aidl aidl名字可以自定义也可以默认 basicTypes是自带的,可以删掉,也可以不删,然后把你自己所需的接口写上去 1.3:创建…

微信小程序开发之后台数据交互及wxs应用

目录 一.后端准备 1.1. 应用配置 1.2. 数据源配置 二、数据库 2.1. 创建 2.2.数据表 2.3.数据测试 三、前端 3.1.请求方法整合 3.2.数据请求 3.3.WXS的使用 3.4.样式美化 3.5. 页面 今天就到这里了哦&#xff0c;希望能帮到你哦&#xff01;&#xff01;&#xf…

解密Java中神奇的Synchronized关键字

文章目录 &#x1f389; 定义&#x1f389; JDK6以前&#x1f389; 偏向锁和轻量级锁&#x1f4dd; 偏向锁&#x1f4dd; 轻量级锁&#x1f4dd; 自旋锁&#x1f4dd; 重量级锁&#x1f525; 1. 加锁&#x1f525; 2. 等待&#x1f525; 3. 撤销 &#x1f389; 锁优化&#x1f…

红队打靶:Misdirection打靶思路详解(vulnhub)

目录 写在开头 第一步&#xff1a;主机发现与端口扫描 第二步&#xff1a;Web渗透&#xff08;80端口&#xff0c;战术放弃&#xff09; 第三步&#xff1a;Web渗透&#xff08;8080端口&#xff09; 第四步&#xff1a;sudo bash提权 第五步&#xff1a;/etc/passwd利…

一文搞懂UART通信协议

目录 1、UART简介 2、UART特性 3、UART协议帧 3.1、起始位 3.2、数据位 3.3、奇偶校验位 3.4、停止位 4、UART通信步骤 1、UART简介 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器&#xff09;是一种双向、串行、异步的通信…

【c++Leetcode】141. Linked List Cycle

问题入口 思想&#xff1a;Floyds Tortoise and Hare 这个算法简单来说就是设置一个慢指针&#xff08;一次移动一个位置&#xff09;和一个快指针&#xff08;一次移动两个位置&#xff09;。在遍历过程中&#xff0c;如果慢指针和快指针都指向同一个元素&#xff0c;证明环…

spacy.load(“en_core_web_trf“)报错TypeError: issubclass() arg 1 must be a class

使用spacy时遇到的问题 写在最前面&#xff1a; 安装spacy和en_core_web_trf时需要保证二者版本一致 安装及查看对应spacy版本 安装 pip install spacy查看版本 import spacy spacy.__version__安装en_core_web_trf 直接安装&#xff08;如果可以的话&#xff09; pytho…

Flutter视图原理之StatefulWidget,InheritedWidget

目录 StatefulElement1. 构造函数2. build3. _firstBuild3. didChangeDependencies4. setState InheritedElement1. Element类2. _updateInheritance3. InheritedWidget数据向下传递3.1 dependOnInheritedWidgetOfExactType 4. InheritedWidget的状态绑定4.1. ProxyElement 在f…

.net6部署到linux上(CentOS Linux 7)

目录 一、先在linux上配置.net环境 添加 Microsoft 包存储库 安装 SDK 安装运行时 检查 SDK 版本可使用终端查看当前安装的 .NET SDK 版本。 打开终端并运行以下命令。 二、创建.net6 mvc项目 并发布 创建项目 修改默认端口 打包发布到文件夹 运行打包项目查看项目是否…

macOS telnet替代方式

前言 经过使用Linux&#xff0c;常常用Linux的telnet查看端口畅通&#xff0c;是否有防火墙&#xff0c;但是在mac上已经没有这个命令了&#xff0c;那么怎么使用这个命令或者有没有其他替代呢&#xff0c;win和linux是否可以使用相同的替代。macOS可以原生用nc命令替代&#…

C算法:使用选择排序实现从(大到小/从小到大)排序数组,且元素交换不可使用第三变量。

需求&#xff1a; 使用选择排序实现从(大到小/从小到大)排序&#xff0c;且元素交换不可使用第三变量 (异或交换法) 代码实现&#xff1a; #include <stdio.h> void maopao(int* array,int len,int(*swap)(int a,int b)) {int i,j;for(i0;i<len-1;i){for(ji1;j<…

【数据结构与算法】two X 树的遍历以及功能实现

前言&#xff1a; 前面我们已经提到过树、二叉树的概念及结构、堆排序、Top-k问题等的知识点&#xff0c;这篇文章我们来详解一下二叉树的链式结构等问题。 &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&#x1f4a5; ✨✨专栏:htt…