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

目录

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

 二,二叉树的遍历

 三,二叉树的基本运算

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.rhkb.cn/news/168749.html

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

相关文章

ResNet论文精读,代码实现与拓展知识

文章目录 ResNet 论文精读代码实现网络可视化代码 拓展知识 ResNets残差的调参残差链接的渊源残差链接有效性的解释ResNet 深度ResNeXt代码实现 能够提点的技巧「Warmup」「Label-smoothing」「Random image cropping and patching」「Knowledge Distiallation」「Cutout」「Ra…

Centos 7 Zabbix配置安装

前言 Zabbix是一款开源的网络监控和管理软件&#xff0c;具有高度的可扩展性和灵活性。它可以监控各种网络设备、服务器、虚拟机以及应用程序等&#xff0c;收集并分析性能指标&#xff0c;并发送警报和报告。Zabbix具有以下特点&#xff1a; 1. 支持多种监控方式&#xff1a;可…

SAP POorPI RFC接口字段调整后需要的操作-针对SP24及以后的PO系统

文章目录 问题描述解决办法 问题描述 在SAP系统的RFC接口结构中添加了字段&#xff0c;RFC也重新引用到了PO系统&#xff0c;Cache和CommunicationChannel都刷新或启停了&#xff0c;但是新增的字段在调用接口的时候数据进不到SAP系统&#xff0c;SAP系统内的值也出不来。经过…

postgresql14-模式的管理(三)

基本概念 postgresql成为数据库管理系统DBMS&#xff0c;在内存中以进程的形态运行起来成为一个实例&#xff0c;可管理多个database。 数据库databases&#xff1a;包含表、索引、视图、存储过程&#xff1b; 模式schema&#xff1a;多个对象组成一个模式&#xff0c;多个模…

nodejs+vue大学生社团管理系统

通过软件的需求分析已经获得了系统的基本功能需求&#xff0c;根据需求&#xff0c;将大学生社团管理系统平台功能模块主要分为管理员模块。管理员添加社团成员管理、社团信息管理&#xff0c;社长管理、用户注册管理等操作。 目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1…

JVM的几个面试重点

JVM的内存区域划分 JVM类加载机制 前言 Java程序最开始是一个 .java 的文件&#xff0c;JVM把它编译成 .closs 文件&#xff08;字节码文件&#xff09;&#xff0c;运行 Java 程序&#xff0c; JVM 就会读取 .class 文件&#xff0c;把文件内容读取到内存中&#xff0c;构造出…

多线程与高并发

1.线程创建的3种方式 2.线程的状态切换步骤 3.线程的5中状态 Java中的线程的生命周期大体可分为5种状态。 1. 新建(NEW)&#xff1a;新创建了一个线程对象。 2. 可运行(RUNNABLE)&#xff1a;线程对象创建后&#xff0c;其他线程(比如main线程&#xff09;调用了该对象的sta…

通义大模型使用指南之通义千问

一、注册 我们可以打开以下网站&#xff0c;用手机号注册一个账号即可。 通义大模型 (aliyun.com) 二、使用介绍 如图&#xff0c;我们可以看到有三个大项功能&#xff0c;通义千问、通义万相、通义听悟。下来我们体验一下通义千问的功能。 1、通义千问 通义千问主要有两个功能…

北邮22级信通院数电:Verilog-FPGA(6)第六周实验:全加器

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 先抄作业&#xff01;&#xff01;&#xff01;&am…

人工智能算法PPT学习

YOLO You only look once 是一种图像识别算法&#xff0c;速度较快。高效、灵活、泛化性能好&#xff0c;在工业中较为受欢迎。 图像金字塔 一幅图像的多个不同分辨率的子图构成的图像集合。是通过一个图像不断的降低采样率产生的&#xff0c;最小的图像可能仅仅有一个像素点…

如何在Ubuntu下安装RabbitMQ服务并异地远程访问?

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

Elasticsearch 8.X 分词插件版本更新不及时解决方案

1、关于 Elasticsearch 8.X IK 分词插件相关问题 球友在 ElasticSearch 版本选型问题中提及&#xff1a;如果要使用ik插件&#xff0c;是不是就使用目前最新的IK对应elasticsearch的版本“8.8.2”&#xff1f; https://github.com/medcl/elasticsearch-analysis-ik/releases/ta…

强大的虚拟机软件 VMware Fusion Pro 13中文最新 for mac

VMware Fusion Pro是一款虚拟化软件&#xff0c;它允许在Mac电脑上同时运行Windows和其他操作系统&#xff0c;而无需重启电脑&#xff0c;它采用了领先的虚拟化技术&#xff0c;能够保证在Mac电脑在同时运行多个操作系统时表现出极高的效率和稳定性。 VMware Fusion Pro具有以…

计算机网络的七层结构、五层结构和四层结构

为什么要分层&#xff1a; 这个就和我们平常写程序一样&#xff0c;高内聚、低耦合。将网络进行分层我们就可以根据每一层的功能分开开发设计&#xff0c;将复杂的网络问题分解为更简单和清晰的小问题&#xff0c;方便设计、实现和标准化。无需在意其他层是如何实现的&#xff…

maven仓库改国内源

今天准备复现漏洞环境&#xff0c;发现太慢&#xff0c;需要配置国内源 file -> settings 搜索maven 修改settings.xml&#xff0c;这里的需要修改两个文件 1.上图的settings.xml文件 2.idea的maven模块 settings.xml文件将原来的注释掉&#xff0c;然后把阿里的添加上&…

保姆级阿里云ESC服务器安装nodejs和服务器node服务管理工具PM2安装使用

一、安装Node到服务器 1. 创建node文件夹 默认 /opt 下边 /opt/node 也可建到其他地方&#xff0c;如/usr/local/node 等 创建后切换到文件夹下 cd /opt/node cd /opt/node2. 下载node并解压 使用命令下载node wget https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-…

PLC单按钮启停算法汇总

单按钮启停在三菱PLC里可以通过简单的取反指令"ALT"实现,西门子PLC如何实现ALT指令,请参考下面文章链接,这篇博客我们汇总常用的单按钮启停实现方法,希望大家读了本篇博客后有所收获。 博途ALT指令 博途S7-1200/1500PLC 取反指令(ALT)-CSDN博客SMART PLC的ALT指…

从0开始学云计算之服务器:服务的定义,特点,应用场景,分类

服务器定义 服务器是计算机的一种。它比普通计算机运行速度更快、负载更高且价格更高。 服务器的英文名称为“Server”&#xff0c;是指在网络上提供各种服务的高性能计算机。作为网络的节点&#xff0c;存储、处理网络上80%的数据、信息&#xff0c;因此也被称为x络的灵魂。 …

UDP网络通信反复发收

package UDP2;import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner;/* * 完成UDP 通信快速入门 实现发1收1*/ public class Client {public static void main(String[] args) throws Exception{// …

2023-1024‍节日(内含表白代码)

文章目录 一、前言二、代码实现三、动态展示四、总结 一、前言 1024可以是计算机操作系统的进制单位&#xff0c;也可以是&#x1f9d1;‍&#x1f4bb;程序员们的特殊纪念日。 每年10月24日被行业认定为“程序员节”。 今天&#xff0c;正是一年一度的“1024程序员节”在此纪…