数据结构-二叉树的前、中、后序遍历

目录

1. 二叉树的遍历

1.1 前序

1.2 中序

1.3 后序

1.4 遍历的复杂度

2.二叉树节点个数及高度的计算

2.1 二叉树节点个数

2.2 二叉树叶子节点的个数

2.3 二叉树高度

2.4 二叉树第k层节点个数


1. 二叉树的遍历

前面的章节中,我们学习了二叉树的顺序结构,二叉树除了顺序结构,还有链式结构,在学链式结构之前,要求深入掌握二叉树的结构,下面我们先来手动快速的创建一个简单的二叉树,方便学习,后面再来研究二叉树的真正创建的方式。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail\n");
		return NULL;
	}
	node->left = NULL;
	node->right = NULL;
	node->data = x;
	return node;
}

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

 下图就是我们上述代码创建的二叉树,从今天开始,我们看到二叉树要将其分为三个部分:根、左子树、右子树

图中每个子树也能再分为根和左子树、右子树,直到不能再分为止。 

二叉树的遍历分为:前序、中序、后序、层序。今天先来学习前中后序,层序后面再学。

1.1 前序

前序要求的访问次序:根、左子树、右子树。 

按照前序的访问规则,对上述代码的节点的访问次序依次是: 1 2 3 null null null 4 5 null null 6 null null

先访问根节点1,然后访问它的左子树,左子树中先访问根节点2,然后访问2的左子树3,3的左右子树是null null,然后继续访问2的右子树为null,接着访问1的右子树,右子树中先访问根节点4,然后访问4的左子树5,再访问5的左右子树null null,接着访问4的右子树6,6的左右子树是null null,所以最终的访问次序是:1 2 3 null null null 4 5 null null 6 null null 

前序的代码实现:

//前序
void PrevOrder(BTNode* root)
{
	if (root==NULL)
	{
		printf("null ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

int main()
{
	BTNode* root = CreatBinaryTree();
	PrevOrder(root);
	printf("\n");
	return 0;
}

打印结果:

递归过程如下:

递归调用的过程实际就是函数栈帧的创建与销毁的过程,每次调用完左子树,它的栈帧就销毁了,调用右子树时会共用左子树的栈帧。

1.2 中序

中序要求的访问次序:左子树、根、右子树

按照中序的访问规则,对上述代码中的节点的访问次序依次是:null 3 null 2 null 1 null 5 null 4 null 6 null

因为每个子树都可以被拆成左子树、根和右子树,而且在访问时左子树的优先级高,左子树可以一直分到3,所以从3的左子树开始访问:null 3 null,然后把null 3 null作为2的左子树,再访问2和2的右子树:null 3 null 2 null,接着把 null 3 null 2 null 作为1的左子树,访问1和1的右子树......,最终访问的次序应该是:null 3 null 2 null 1 null 5 null 4 null 6 null

中序代码实现:

//中序
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("null ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

int main()
{
	BTNode* root = CreatBinaryTree();
	//前序
	PrevOrder(root);
	printf("\n");
	//中序
	InOrder(root);
	printf("\n");
	return 0;
}

运行结果:

1.3 后序

后序要求的访问次序:左子树、右子树、根。 

按照后序的访问规则,对上述代码中的节点的访问次序依次是:null null 3 null 2 null null 5 null null 6 4 1。 与分析前序和中序一样,这里不再详解。

后序代码实现:

//后序
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("null ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	//前序
	PrevOrder(root);
	printf("\n");
	//中序
	InOrder(root);
	printf("\n");
	//后序
	PostOrder(root);
	printf("\n");
	return 0;
}

运行结果:

以上就是二叉树的前、中、后序遍历了,这几种方式其实就是对根的访问的先后问题,如果上述内容还不是很明白,最好画一下递归调用图,这样就很清楚了。

1.4 遍历的复杂度

时间复杂度:O(N),因为二叉树一共有N个节点,递归一共调用N次,所以时间复杂度是O(N)。

空间复杂度:O(h),h的范围是:[ logN, N ]

为什么空间复杂度是这样的呢?

我们前面的章节中讲过,时间是一去不复返的,所以时间要累加计算,而空间是可以共用的,所以空间不能累加计算。我们在调用函数时,左子树调用完,它的栈帧会销毁,而调用右子树时,它会共用左子树的栈帧,而假设二叉树有N个节点,当它是满二叉树时,由于左右子树共用一个空间,只需创建空间logN次,而如果二叉树像下图中的情况,它就要创建空间N次,所以空间复杂度是:O(logN~N)

2.二叉树节点个数及高度的计算

2.1 二叉树节点个数

法一:

要计算二叉树节点个数,我们只需要将二叉树遍历一遍(前、中、后序都可以),每次调用时使size++即可,注意size要定义为全局变量,防止每次调用的时候size被置为0

代码如下:

//二叉树节点个数
int size = 0;
int BTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	size++;
	BTreeSize(root->left);
	BTreeSize(root->right);
}

int main()
{
	BTNode* root = CreatBinaryTree();
	BTreeSize(root);
	printf("BTreeSize:%d\n", size);
	return 0;
}

法二:

把计算节点个数分为,左子树节点个数+右子树节点个数+根节点个数,而每个子树还能分为左子树、右子树和根,所以我们使用递归的思想,如果根节点不为空,就分别计算它的左右子树节点个数+它自身,如果为空,就返回0。

代码如下:

//二叉树节点个数
int BTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return  BTreeSize(root->left) + BTreeSize(root->right)+1;
}

int main()
{
	BTNode* root = CreatBinaryTree();
	printf("BTreeSize:%d\n", BTreeSize(root));
	return 0;
}

运行结果:

2.2 二叉树叶子节点的个数

要计算叶子节点,也可以使用上述分开计算的方法,分别计算左子树和右子树的叶子节点个数,然后相加,递归的条件是:如果左子树和右子树的节点都是NULL,那说明是叶子节点,返回1,否则,说明是分支节点,继续往下递归

代码如下:

//二叉树叶子节点
int BLeafNum(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1 ;
	}
	return BLeafNum(root->left) + BLeafNum(root->right);
}

int main()
{
	BTNode* root = CreatBinaryTree();
	printf("BLeafNum:%d\n", BLeafNum(root));
	return 0;
}

运行结果:

2.3 二叉树高度

 求二叉树高度,也可以分别求左子树和右子树的高度,然后比较大小,返回大的值,并将该值加一就是二叉树的高度加一是因为左右子树距离根节点还有一层

求左右子树的高度可以再分解为上面的步骤,所以使用递归解决问题。

代码如下:

//二叉树高度
int BTreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int LeftNum = BTreeHeight(root->left);
	int RightNum = BTreeHeight(root->right);
	return LeftNum > RightNum ? LeftNum + 1 : RightNum + 1;
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf(" BTreeHeight:%d\n", BTreeHeight(root));

	return 0;
}

 运行结果:

2.4 二叉树第k层节点个数

该问题可以转换成:分别求左子树的第k-1层和右子树的第k-1层,然后返回它们的和

结束条件是:k==1 且k不为空

比如我们要求1的第三层,就是求2和4的第二层,也就是求3 5 6的第一层

代码如下:

//二叉树第k层节点的个数
int BTreekNum(BTNode* root,int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BTreekNum(root->left, k - 1) + BTreekNum(root->right, k - 1);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf("BTreekNum:%d\n", BTreekNum(root,3));
	printf("BTreekNum:%d\n", BTreekNum(root, 2));
	return 0;
}

运行结果:

递归过程如下:

通过以上计算,相信我们对二叉树的遍历有了更深的理解,同时也加深了对递归的理解,其实当我们熟练运用递归之后,递归问题都可以分两步解决:1. 找出子问题  2. 递归条件

 

以上就是今天学习的所有内容了,未完待续。。。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python入门:一文详解Python列表(List)操作方法

文章目录 前言一、创建一个列表二、访问列表中的值三、更新列表四、删除列表元素六、Python列表截取七、Python列表操作的函数和方法关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②…

【thop.profile】thop.profile计算网络参数量和计算效率

&#x1f34b;&#x1f34b;1.安装thop 安装thop有两种方式。 &#x1f3c6;第一种 pip install thop &#x1f3c6;第二种 用源码编译安装 从官网下载【github】thop安装压缩包下载压缩文件&#xff0c;解压到虚拟环境的site-packages文件下激活进入自己的虚拟环境cd到压缩…

不同类型的软件企业该如何有效的管理好你的软件测试团队?

最近在网上发现一篇记录了2012年《[视频]作为测试经理如何有效管理好你的软件测试团队》的文字内容&#xff0c;感谢记录的人&#xff0c;我也保存一下。顺便将演讲中的PPT重点截图也放上来&#xff0c;一并保存了&#xff01;。由于是现场速记&#xff0c;过度的口语化&#x…

迅为iTOPRK3588开发板系统定制(无法联网)

在上一个小节中讲解了 ubuntu 和 debian 文件系统的定制&#xff0c;但那是在可以运行脚本正常构 建系统的前提下&#xff0c;而本小节则是针对部分特殊用户无法联网的情况。 在 source 目录下存放了已经构建完成的压缩包&#xff0c;如下图所示&#xff1a; 然后使用以下命令…

weblogic控制台登陆console的时候慢

我们在搭建完weblogic后&#xff0c;登录控制台时&#xff0c;会出现等待很长时间的情况。 如下图&#xff1a;怎么解决呢 连接所属服务器,.找到jdk的安装路径 [rootlocalhost lib]# echo $JAVA_HOME/ /usr/java/jdk1.8.0_161/ 进入jre下的lib目录下的security目录&#xff0…

全球市场的新趋势:海外网红营销和私域流量的共同驱动

在数字时代的今天&#xff0c;随着全球互联网的蓬勃发展&#xff0c;网络营销已经不再是一种新鲜事物。然而&#xff0c;随着社交媒体和在线内容创作的兴起&#xff0c;一种新的营销方式崭露头角&#xff0c;它将海外网红营销与私域流量相结合&#xff0c;成为了全球市场的一股…

刘家窑中医院医生王忠:以仁心诠释医者使命

王忠是刘家窑中医院的一名医生&#xff0c;从医多年&#xff0c;积累了丰富的临床经验&#xff0c;挽救了无数病人的生命。他以“想病人之所想&#xff0c;急病人之所急”为自己行医济世的人生格言。 病人说&#xff1a;他是一位可亲可敬的“亲人”。 “医以德为本&#xff0c…

【Proteus仿真】【STM32单片机】拔河游戏设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用按键、LED、动态数码管模块等。 主要功能&#xff1a; 系统运行后&#xff0c;指示灯处于中间位置&#xff0c;数码管显示得分0&#xff0c;当…

MySQL主从复制、读写分离(利用Amoeba和Mycat)、完全同步

目录 一、主从复制 1、概念 1.1主从复制延迟问题&#xff1a; 1.2、MySQL安全和性能配置&#xff1a; 1.3、主从复制的工作过程&#xff1a; 1.4、mysql主从复制注意点&#xff1a; 1.5、MySQL的主从复制的模式&#xff1a; 2、主从复制实验&#xff1a; 二、读写分离&…

【ccf-csp题解】第11次csp认证-第三题-Json查询超详细讲解

此题思路来源于acwing ccfcsp认证辅导课 题目描述 思路分析 此题的难点在于对输入的内容进行解析&#xff0c;题目中说除了保证字符串内容不会有空格存在之外&#xff0c;其它的任意地方都可能出现空格&#xff0c;甚至在某些地方还会出现空行&#xff0c;这样的话&#xff0…

由于找不到msvcp140.dll无法继续执行代码有哪些解决方法

msvcp140.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它是运行许多Windows应用程序所必需的。当msvcp140.dll丢失或损坏时&#xff0c;可能会导致以下问题&#xff1a; 1. 程序无法启动或崩溃。 2. 系统出现错误提示&#xff0c;如“找不到msvcp140…

离散卡尔曼滤波器算法详解及重要参数(Q、R、P)的讨论

公开数据集中文版详细描述参考前文&#xff1a;https://editor.csdn.net/md/?not_checkout1&spm1011.2124.3001.6192神经元Spike信号分析参考前文&#xff1a;https://blog.csdn.net/qq_43811536/article/details/134359566?spm1001.2014.3001.5501神经元运动调制分析参考…

Java学习之路 —— 异常、集合

文章目录 1. 异常2. 集合2.1 遍历2.1.1 迭代器2.1.2 增强for循环2.1.3 Lambda 2.2 List2.3 Set2.3.1 HashSet2.3.2 LinkedHashSet2.3.3 TreeSet 2.4 Map 1. 异常 Exception&#xff1a;叫异常&#xff0c;是程序员可以捕捉的。异常又分为了2类&#xff1a; 运行时异常&#x…

SpringBoot整合WebSocket实现订阅消息推送

目录 一、什么是WebSocket1.HTTP协议2.WebSocket协议 二、WebSocket使用场景1.消息推送2.实时聊天3.弹幕4.实时数据更新 三、SpringBoot整合WebSocket&#xff08;以实现消息推送为例&#xff09;1.添加依赖2.创建消息类2.WebSocket配置类3.工具类4.测试连接5.服务调用 一、什么…

人工智能 :一种现代的方法 第七章 逻辑智能体

文章目录 前言人工智能 &#xff1a;一种现代的方法 第七章 逻辑智能体7.1 基于知识的智能体7.2 Wumpus世界7.4 命题逻辑7.5 命题逻辑定理证明7.5.1推导和证明7.5.2 归结原理7.5.3 horn子句和限定子句7.5.4 前向链接和后向链接 7.6 有效命题逻辑模型求解7.6.1完备的回溯算法7.6…

MySQL主从延时问题

过线上 MySQL 维护经验的童鞋都知道&#xff0c;主从延迟往往是一个让人头疼不已的问题。 不仅仅是其造成的潜在问题比较严重&#xff0c;而且主从延迟原因的定位尤其考量 DBA 的综合能力&#xff1a;既要熟悉复制的内部原理&#xff0c;又能解读主机层面的资源使用情况&#…

使用PHP编写采集药品官方数据的程序

目录 一、引言 二、程序设计和实现 1、确定采集目标 2、使用PHP的cURL库进行数据采集 3、解析JSON数据 4、数据处理和存储 5、数据验证和清理 6、数据输出和可视化 7、数据分析和挖掘 三、注意事项 1、合法性原则 2、准确性原则 3、完整性原则 4、隐私保护原则 …

如何在 Windows 10/11 上高质量地将 WAV 转换为 MP3

WAV 几乎完全准确地存储了录音硬件所听到的内容&#xff0c;这使得它变得很大并占用了更多的存储空间。因此&#xff0c;WAV 格式在作为电子邮件附件发送、保存在便携式音频播放器上、通过蓝牙或互联网从一台设备传输到另一台设备等时可能无法正常工作。 如果您遇到 WAV 问题&…

【Android Studio调试报错】setContentView(R.layout.activity_main);

报错如下&#xff1a; 解决方法&#xff1a; 1、把参数删除到只剩 .&#xff0c;用自动补齐的方式来查看当前文件的位置是不是&#xff0c;当前左侧工程中layout 所在的位置。在的话它会在自动补齐列表有选项。否则我们选中第一个。 2、选中之后是这样的 然后问题解决&#xf…

飞天使-url路由进阶应用

url 的name属性 为 app01的url设定命名空间 app_name app01urlpatterns [path(, views.index, nameindex),path(login/, views.login, namelogin), ] 上面的namelogin 同时 from django.shortcuts import render,reverse,redirect 便于项目引用 def index(request):userna…