数据结构初阶 · 链式二叉树的部分问题

目录

前言:

1 链式二叉树的创建

2 前序 中序 后序遍历

3 树的节点个数

4 树的高度

5 树的叶子节点个数

6 树的第K层节点个数


前言:

链式二叉树我们在C语言阶段已经实现了,这里介绍的是涉及到的部分问题,比如求树的高度,求树的节点个数等,连接部分就手动连接,用一个样例来介绍涉及到的几个问题。

这里对前面知识反馈比较大的是递归,可以说每个问题都用到了递归。


1 链式二叉树的创建

因为是链式二叉树,所以有两个指针,分别指向右孩子节点和左孩子节点,给上值,手动连接即可:

typedef struct TreeNode
{
	struct TreeNode* left = NULL;
	struct TreeNode* right = NULL;
	int data;
}TreeNode;

TreeNode* Tree()
{

	TreeNode* node1 = new TreeNode;
	TreeNode* node2 = new TreeNode;
	TreeNode* node3 = new TreeNode;
	TreeNode* node4 = new TreeNode;
	TreeNode* node5 = new TreeNode;
	TreeNode* node6 = new TreeNode;
	//TreeNode* node7 = new TreeNode;

	node1->data = 1;
	node2->data = 2;
	node3->data = 3;
	node4->data = 4;
	node5->data = 5;
	node6->data = 6;
	//node7->data = 7;

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

整体构建出来就是这样,对于初学链式二叉树的同学来说,NULL给上是很有必要的,这对于后面的遍历问题有帮助。


2 前序 中序 后序遍历

前序遍历的顺序是 根 左子树 右子树,中序遍历的的顺序是左子树 根 右子树 ,后序遍历的顺序是左子树,右子树,根。

咱们从前序开始,顺便进行打印,从1开始,到左子树是2,2的左子树是3,3是左子树是NULL,再往下就没了,递归回来是3的右子树,那么此时2的左子树就遍历完成了,2的右子树是NULL,这时候1的左子树就遍历完了,然后是右子树,到4,然后是4的左子树5,接着是5的左右子树,这时4的左子树就遍历完了,然后是右子树,当6遍历完了之后,才是整个树都遍历完了。

那么代码会不会很复杂呢?

不会,递归都有一个特点,思路难想,代码简单:

void PrevOrder(TreeNode* root)
{
	if (root == NULL)
	{
		cout << "N ";
		return;
	}
	cout << root->data << " ";
	PrevOrder(root->left);
	PrevOrder(root->right);
}

这就遍历完了。

打印的结果是1 2 3 N N N 4 5 N N 6 N N,那么为了加强理解我们可以这样看:

3 N N N是2的左右子树,5 N N 6 N N是4的左右子树,2 4 是1 的左右子树,这样结合起来就好理解多了。

那么对于中序后序来说都是一样的,这里给代码,就不重复演示了:

void InOrder(TreeNode* root)
{
	if (root == NULL)
	{
		cout << "N ";
		return;
	}
	InOrder(root->left);
	cout << root->data << " ";
	InOrder(root->right);
}

void BackOrder(TreeNode* root)
{
	if (root == NULL)
	{
		cout << "N ";
		return;
	}
	BackOrder(root->left);
	BackOrder(root->right);
	cout << root->data << " ";
}

3 树的节点个数

树的节点个数问题,使用的是分而治之的思想,比如一个院,要统计有多少人,那么院长就发号司令,副院长去问班主任,班主任去问辅导员,辅导员去问班长,然后加上自己,最后就可以得到总总共的人数。

树的节点个数是一样的,求总节点个数,我们可以把树分为左右子树,把一个树拆分成无数的左右子树,统计每个左右子树的节点个数,相加即可。

代码实现:

size_t TreeSize(TreeNode* root)
{
	return root == NULL? 0 : 
		TreeSize(root->left) + TreeSize(root->right) + 1;
}

是的,代码就这么简单,这里给上递归展示图看看:

这里递归的是1的右子树。


4 树的高度

树的高度同理,我们可以理解为两个院的人比最高的,树的高度即我们同理,返回左右子树高度最高的即可,因为一个节点本身就算1,所以返回高度的时候需要加1,返回的条件就是节点为空,为空就返回0:

size_t TreeHeightError(TreeNode* root)
{
	return root == NULL ? 0 : 
		TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

以上代码看起来是没问题的,100个样例能跑过90的样例,但是数据量一多就会崩盘,因为这里没有记录左右子树的高度,也就是说会重复计算,这里不要小看重复计算,没有记录那么所有的计算都会翻二倍,而每一层都没有记录,所有越往层数多的走,就会变成2^n的重复计算,是指数量级的增长,所以我们应该记录数据:

size_t TreeHeight(TreeNode* root)
{
	if (root == NULL)	{
		return 0;
	}
	size_t leftHeight = TreeHeight(root->left);
	size_t rightHeight = TreeHeight(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

5 树的叶子节点个数

叶子节点即没有孩子节点的孩子,那么返回1的条件就是左右孩子都为空,如果不为空往下遍历就ok了:

size_t TreeLeaf(TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeaf(root->left) + TreeLeaf(root->right);
}

6 树的第K层节点个数

这是本文最难的一个问题了,该问题的子问题是:

空节点的时候返回0,k = 1的时候返回1,K > 1的时候遍历即可,个人认为k - 1是这个问题的关键所在:

size_t TreeNodeK(TreeNode* root,size_t k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeNodeK(root->left, k - 1) +
		TreeNodeK(root->right, k - 1);
}

因为遍历是从根节点开始遍历的,所以减到k = 1的时候,是涉及到的那层,对该层进行判断,这是存在一个顺序问题,root == NULL应该在k == 1之前,因为递归到那一层,k = 1是铁定的,这时候就需要先判断是不是空节点,不是空节点再讨论K= 1的问题。

以上所有问题的测试代码:

int main()
{
	TreeNode* node = Tree();
	size_t k = 2;
	//前序遍历
	PrevOrder(node);
	cout << endl;
	//中序遍历
	InOrder(node);
	cout << endl;
	//后序遍历
	BackOrder(node);
	cout << endl;
	//树的节点个数
	cout << "The Tree's size is:" << 
		TreeSize(node) << endl;
	//树的高度
	cout << "The Tree's Height is:" <<
		TreeHeightError(node) << endl;
	//树的叶子节点个数
	cout << "The Tree's leaf is:" <<
		TreeLeaf(node) << endl;
	//树的第K层节点个数
	cout << "The Tree's leaf about K is:" <<
		TreeNodeK(node,k) << endl;

	return 0;
}

感谢阅读!

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

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

相关文章

liquibase做数据库版本管理

通过这个配置就会自动启动liquibase 比对 https://www.cnblogs.com/ludangxin/p/16676701.html https://zhuyizhuo.github.io/2020/07/04/spring-boot/spring-boot-liquibase-database-version-control/

如何理解与学习数学分析——第二部分——数学分析中的基本概念——第10章——实数

第2 部分&#xff1a;数学分析中的基本概念 (Concepts in Analysis) 10. 实数(The Real Numbers) 本章介绍比率数(rational numbers)和非比数(irrational numbers)及其与十进制展开的关系。讨论了实数的公理&#xff0c;并解释了完备性公理对于区分实数和比率数为何必不可少&…

IDEA启动项目报java.lang.OutOfMemoryError: GC overhead limit exceeded

idea编译项目时报j ava.lang.OutOfMemoryError: GC overhead limit exceeded错误&#xff0c;教你两步搞定&#xff01; 第一步&#xff1a;打开help -> Edit Custom VM Options ,修改xms和xmx的大小&#xff0c;如下图&#xff1a; 第二步&#xff1a;File -> Settings…

基于JSP的足球赛会管理系统

你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 个人中心 球队介绍…

使用 Sysbench 测试文件的读写速度

要使用 Sysbench 测试文件的读写速度&#xff0c;你可以按照以下步骤进行&#xff1a; 安装 Sysbench&#xff1a; 如果你还没有安装 Sysbench&#xff0c;可以通过以下命令在 Ubuntu 上安装&#xff1a; sudo apt install sysbench创建测试文件&#xff1a; 首先&#xff0c…

Xilinx(AMD) vivado对FPGA网表文件进行功能仿真的方法

1 概述 在FPGA开发中很多商用IP核出于知识产权保护的目的&#xff0c;不提供源代码&#xff0c;而是提供综合后的FPGA网表。由于没有源代码&#xff0c;也无法对网表文件直接进行仿真的操作来验证功能&#xff0c;此时需要独立的仿真模型文件。 本文介绍在Xilinx(AMD) vivado软…

LVGL移植和图片显示

最近闲来无事&#xff0c;偶尔刷到了移植LVGL的教程&#xff0c;今天肝完了机械原理又移植完LVGL库&#xff0c;真是收获满满的一天&#xff0c;先接一杯水去。 回来了&#xff0c;发个朋友圈高级一下&#xff0c;好困。 lvgl v8.3移植及组件使用_lvgl界面编辑器-CSDN博客htt…

MySQL限制登陆失败次数配置

目录 一、限制登陆策略 1、Windows 2、Linux 一、限制登陆策略 1、Windows 1&#xff09;安装插件 登录MySQL数据库 mysql -u root -p 执行命令安装插件 #限制登陆失败次数插件 install plugin CONNECTION_CONTROL soname connection_control.dll;install plugin CO…

英伟达:史上最牛一笔天使投资

200万美元的天使投资&#xff0c;让刚成立就面临倒闭风险的英伟达由危转安&#xff0c;并由此缔造了一个2.8万亿美元的市值神话。 这是全球风投史上浓墨重彩的一笔。 前不久&#xff0c;黄仁勋在母校斯坦福大学的演讲中&#xff0c;提到了人生中的第一笔融资——1993年&#x…

离散数学答疑 5

知识点&#xff1a;单侧连通&#xff0c;强连通&#xff0c;弱连通 前缀码&#xff1a;比如001和00101就不是。因为后者的前三位和前者的重复了 有向图的邻接矩阵求法&#xff1a;横着看 数据结构21-4分钟搞定邻接矩阵_哔哩哔哩_bilibili 可达矩阵是包含自反性的。可达矩阵是…

Objective-C的初始化方法中,应该如何读写属性

除非有明确的原因需要使用setter, getter, 否则总是应该直接访问, 也就是直接使用实例变量&#xff08;也称为 iVar&#xff09;来读写数据 理由&#xff1a; 避免子类覆盖setter方法的影响&#xff1a;若在初始化方法中使用setter方法, 使用此方法实例化子类, 可能会调用子类…

DeepSpeed Learning Rate Scheduler

Learning Rate Range Test (LRRT) 训练试跑&#xff0c;该lr scheduler从小到大增长lr&#xff0c;同时记录下validatin loss&#xff1b;人来观察在训练多少step之后&#xff0c;loss崩掉&#xff08;diverge)了&#xff0c;进而为真正跑训练&#xff0c;挑选合适的lr区间&…

帕友的小贴士,锻炼

帕金森病作为一种慢性神经系统疾病&#xff0c;对患者的生活质量产生了深远的影响。虽然医学界对于帕金森病的治疗仍在不断探索&#xff0c;但合理的锻炼已经被证实是改善患者症状、提高生活质量的有效途径之一。本文旨在为帕金森病患者推荐一些适合的锻炼方法&#xff0c;帮助…

57.Semaphore信号量

用来限制能同时访问共享资源的线程上限。只是适合限制单机线程数量。 Slf4j public class SemaphoreDemo {public static void main(String[] args) {Semaphore semaphore new Semaphore(3);for (int i 0; i < 10; i) {new Thread(() -> {try {semaphore.acquire();//…

【C++类和对象中篇】(构造函数和析构函数)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 &#x1f369;1.默认成员函数的概念&#xff1a; &#x1f369;2.构造函数&#xff1a; 2.1特性&…

深度学习模型的生命周期与推理系统架构

目录 深度学习模型的生命周期 ​编辑 深度学习模型的生命周期 推理相比训练的新特点与挑战 推理系统架构 推理系统 vs 推理引擎 顶层:API接口和模型转换 中层:运行时(计算引擎) 底层:硬件级优化 边缘设备计算 主要问题 边缘部署和推理方式 方式1:边缘设备计…

问题汇总:MPU6050(软件iic)

以下为个人问题汇总&#xff0c;排查点汇总可能大有缺陷&#xff0c;如有错误&#xff0c;欢迎指正。 排查点汇总 检查软件iic的时序操作用示波器或逻辑分析仪检查波形 无法使用逻辑分析仪进行I/O引脚波形分析 充当SDA、SCL的引脚要配置为推挽输出; 另外&#xff0c;逻辑分…

纳什均衡:博弈论中的运作方式、示例以及囚徒困境

文章目录 一、说明二、什么是纳什均衡&#xff1f;2.1 基本概念2.2 关键要点 三、理解纳什均衡四、纳什均衡与主导策略五、纳什均衡的例子六、囚徒困境七、如何原理和应用7.1 博弈论中的纳什均衡是什么&#xff1f;7.2 如何找到纳什均衡&#xff1f;7.3 为什么纳什均衡很重要&a…

Ubuntu 22.04安装cuda及Pytorch教程

文章目录 1、安装显卡驱动2、安装CUDA3、安装cuDNN4、安装pyTorch5、卸载CUDA参考资料 服务器重装系统后&#xff0c;需要重新安装显卡驱动、cuda及Pytorch等&#xff0c;有些步骤容易忘记&#xff0c;这里记录一下。这里我的服务器配置以及安装版本的情况如下&#xff1a; 服…

OpenGauss数据库-5.数据更新

第1关&#xff1a;插入数据 gsql -d postgres -U gaussdb -W "passwd123123" create table student (id integer primary key,name char(20),age integer ); insert into student values(1,"lily",20),(2,lily,21),(3,marry,19); 第2关&#xff1a;删除数…