二叉树结点个数、叶子结点个数、树的高度、第k层结点个数的计算(C语言)

news2024/11/18 23:47:54

目录

前言

分治算法

模拟二叉树代码

结点个数计算

错误方法

不便利方法

基于分治思想的方法

叶子结点个数

树的高度

第k层结点的个数


前言

        在链式二叉树的前序、中序、后续遍历中我们模拟了一棵二叉树,并实现了它的前、中、后序遍历,现在我们来解决设计与二叉树相关的计算问题。

分治算法

概念:是一种将问题划分为更小的子问题,并通过解决子问题来解决原始问题的算法设计策略

分治算法的基本思想:

  1. 分解:将原始问题划分成若干个规模较小且相互独立的子问题。这里关键是要找到一个适当的方式将原始问题切割成更小规模的子问题,使得每个子问题都与原始问题具有相同或类似结构。

  2. 解决:递归地求解各个子问题。对于规模较小而直接可求解的情况,直接给出答案;对于规模较大而无法直接求解时,则继续应用该算法来进一步划分为更小的子问题并进行求解。

  3. 合并:将各个子结果合并成最终结果。在所有子任务都被独立地处理和求得结果之后,需要把这些局部结果合并起来以获得整体上正确且有效率的最终输出。

可以应用分治算法来解决的问题的特点:

1、原问题可以分解为多个子问题

子问题与原问题相比,只是问题的规模有所降低,其结构和求解方法与原问题相同或相似

2、原问题在分解过程中,递归地求解子问题

由于递归都必须有一个终止条件,故当分解后的子问题规模足够小时,应能够直接求解

3、在求解并得到各个子问题的解后

应能够采用某种方式、方法合并或构造出原问题的解

结论:不难发现,在分治策略中,由于子问题与原问题在结构和解法上的相似性,用分治方法解决的问题,大都采用了递归的形式🥰

模拟二叉树代码

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int BTDataType;
 
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}TreeNode;
 
TreeNode* BuyTreeNode(int x)
{
	TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
	assert(node);
 
	node->data = x;
	node->left = NULL;
	node->right = NULL;
 
	return node;
}
 
TreeNode* CreatTree()
{
 
	TreeNode* node1 = BuyTreeNode(1);
 
	TreeNode* node2 = BuyTreeNode(2);
 
	TreeNode* node3 = BuyTreeNode(3);
 
	TreeNode* node4 = BuyTreeNode(4);
 
	TreeNode* node5 = BuyTreeNode(5);
 
	TreeNode* node6 = BuyTreeNode(6);
 
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	//node2->right = NULL;
	//node3->left = NULL;
	//node3->right = NULL;
	node4->left = node5;
	node4->right = node6;
	//node5->left = NULL;
	//node5->right = NULL;
	//node6->left = NULL;
	//node6->right= NULL;
 
	return node1;
}
 
int main()
{
	TreeNode* root = CreatTree();
	return 0;
}

结点个数计算

错误方法

int TreeSize(TreeNode* root)
{
	if (root == NULL)
		return;
	int size = 0;
	++size;

	TreeSize(root->left);
	TreeSize(root->right);
	return size;
}

        这是因为每一次的递归都会开辟出一个帧栈,而每一块的帧栈中都会有一个size且声明周期仅只在自己的帧栈范围内,在调用返回时所有的size并不会相加然后一起返回,简单来说就是生命周期有限

不便利方法

//二叉树代码
.....

static int size = 0;
int TreeSize(TreeNode* root)
{
	if (root == NULL)
		return;
	++size;

	TreeSize(root->left);
	TreeSize(root->right);

	return size;
}

int main()
{
	TreeNode* root = CreatTree();
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	return 0;
}

        基于上次生命周期的问题,我们想到了用static来延长局部变量的生命周期,此时size的生命周期就是整个程序,但是当我们连续三次打印时发现三次的结果都不一样,每次都比上次的结果增加了6?这也是使用static的副作用,因为被static修饰的变量(静态变量)在整个程序中只会初始化一次,当第二次使用该静态变量时,此次的结果与上次的结果叠加,从而出现意料之外的问题

我们需要在首次打印后,后续的每次打印前将该静态变量人为置空后才能正常使用:

//二叉树代码
.....

static int size = 0;
int TreeSize(TreeNode* root)
{
	if (root == NULL)
		return;
	++size;

	TreeSize(root->left);
	TreeSize(root->right);

	return size;
}

int main()
{
	TreeNode* root = CreatTree();
	printf("TreeSize:%d\n", TreeSize(root));
    
    size = 0;
	printf("TreeSize:%d\n", TreeSize(root));

    size = 0;
	printf("TreeSize:%d\n", TreeSize(root));
    
    size = 0;
	printf("TreeSize:%d\n", TreeSize(root));
	return 0;
}


使用全局变量也是一样的效果,只需要对代码进行简单的更改即可:

//二叉树代码
.....

int size = 0;
void TreeSize(TreeNode* root)
{
	if (root == NULL)
		return;
	++size;

	TreeSize(root->left);
	TreeSize(root->right);
}


int main()
{
	TreeNode* root = CreatTree();
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);
	return 0;
}

基于分治思想的方法

//二叉树总结点个数
int TreeSize(TreeNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) +
		TreeSize(root->right) + 1;
}

解释:主体逻辑就是判断此时所处结点的值是否为空,若不为空则进行左递归,左递归结束后进行右递归,每一次左右递归完全结束后就会返回一个1,每次返回的结果可以叠加

叶子结点个数

//叶子结点个数
int TreeLeafSize(TreeNode* root)
{
	//空树 返回0
	if (root == NULL)
		return 0;

	//非空树,但是没有左右子树(叶子结点/仅有一个结点的树)返回1
	if (root->left == NULL && root->right == NULL)
		return 1;

	//不是空 也不是叶子结点
	return TreeLeafSize(root->left) +
		TreeLeafSize(root->right);
}

解释:主体逻辑与获取结点总数时没有区别,但是这里增加了一个用于判断叶子结点的判断条件,因为叶子结点的特殊性(没有左右子树),所以我们的返回1的操作操作必须要在确定该结点为叶子结点时才会返回

树的高度

//树的高度
int TreeHeight(TreeNode* root)
{
	if (root == NULL)
		return 0;
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

设计思想:

  1. 如果即当前子树为空,则返回 0,表示该子树没有任何结点,因此高度为 0
  2. 如果传入的 root 指针不为空,则执行以下操作:
    a)调用递归函数 TreeHeight(root->left) 来计算左子树中结点到达最底层所需经过的边数,并将结果赋值给变量 leftHeight
    b)调用递归函数 TreeHeight(root->right) 来计算右子树中结点到达最底层所需经过边数,并将结果赋值给变量 rightHeight
    c)返回左右子树两者中较大者加上当前节点本身所代表边数(加1)作为该子问题下一级别解答(理解这里十分重要)

解释: 1、某个结点的左子树递归的三目运算符的运算结果都会在最后赋值给leftHeight,右子树递归的三目运算符的运算结果都会在最后赋值给rightHeight

2、每次调用 TreeHeight 函数时都会进行三目运算符的比较,如果是叶子结点,由于没有左右子树为空所以两次递归返回的值均为0,即leftHeight和rightHeight的值均为0(因为0>0为假,故:rightHeight+1,此时rightHeight+1里的+1是为了加上3结点本身的高度,不可能因为左右子树均为空就没有高度了,当前结点也算一个高度)比较后会返回1它被赋值给leftHeight,因为它是2结点的左子树递归得到的,同时它也告诉了2结点你的左子树高度只有1,然后2结点又会递归调用它的右子树但是由于右子树为NULL所以会返回0,所以此时leftHeight和rightHeight的值分别为1和0(因为1>0为真所以leftHeight+1,表明2结点的左子树大于右子树,左子树的高度可以代表2结点的高度)比较后会返回2它被赋值给leftHeight,因为它是1结点的左子树递归得到的,同时它也告诉了1结点你的左子树高度为2

3、然后1结点会递归它的右子树,剩余步骤与上面描述的大致相似,最后右递归会告诉1结点你的右子树高度为2(因为2>2为假所rightHeight+1,此时rightHeight+1里的+1是为了加上1结点本身的高度,不可能左右子树高度相等就没有高度了,当前结点也算一个高度,所以当前结点的左右子树结点高度相等,将当前左右结点子树的高度+1就是整个树的高度)

第k层结点的个数

//第k层结点的个数,k==2
int TreeLevelK(TreeNode* root,int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;

	return TreeLevelK(root->left, k - 1) +
		TreeLevelK(root->right,k-1);
}

 设计思想:

  1. 树为空,返回0
  2. 树不为空且是第一层结点个数,返回1
  3. 树不为空且是第n(n>1)层结点的个数,返回(左子树的k-1层 + 右子树的k-1层)

k-1而不是k,k相当于判断条件count,当count==1的时候就相当于找到了我们要找的那一层,如果为k,那么递归的返回条件就不存在

解释:

1、查找第3层结点个数,即k == 3 

2、树不为空,并且k != 1,所以递归结点1的左子树,结点2不为空,此时k-1 = 2 != 1,所以可以继续递归结点2的左子树,结点3不为空,此时k-1 = 1 == 1,所以返回1,即递归结点2左子树的返回值为1,然后递归结点2的右子树,右子树为空返回0,最后0+1=1,即递归结点1左子树的返回值为1,这说明结点1左子树第3层的结点个数为1

3、然后递归结点1的右子树,结点4不为空,此时k-1 = 2 != 1,所以可以继续递归结点4的左子树,结点5不为空,此时k - 1 =1 == 1,所以返回1,即递归结点4的左子树的返回值为1,然后递归结点4的右子树,结点6不为空,此时k - 1 =1 == 1(这是因为此时处于结点4的TreeLevelK函数中,此时的k为2),所以返回1,即递归结点4的右子树的返回值为1,最后1+1=2,即递归结点1右子树的返回值为2,这说明结点1右子树第3层的结点个数为2

4、最后结点1的左右子树均递归完璧,1+2=3,即该二叉树第3层的结点个数为3

 ~over~

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

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

相关文章

Leetcode—231.2的幂【简单】

2023每日刷题&#xff08;五十四&#xff09; Leetcode—231.2的幂 实现代码 class Solution { public:bool isPowerOfTwo(int n) {if(n < 0) {return false;}long long ans 1;while(ans < n) {ans * 2;}if(ans n) {return true;}return false;} };运行结果 之后我会…

Echarts饼图中显示百分比

开发中遇到一个需求&#xff0c;要在饼图上显示数据百分比&#xff0c;下图&#xff1a; 查了echarts 文档&#xff0c;并不能通过简单的配置来实现&#xff0c;原因如下&#xff1a;在单个serie的label中&#xff0c;只能设置一个label&#xff0c;位置可以选择在饼图内部inne…

坚鹏:兴业银行EAST5.0政策解读及数据质量提升方案培训

兴业银行股份有限公司&#xff08;简称“兴业银行”&#xff09;成立于1988年8月&#xff0c;2022年总资产9.27万亿元&#xff0c;是经国务院、中国人民银行批准成立的首批股份制商业银行之一&#xff0c;总行设在福州市。现已发展成为横跨境内外&#xff0c;线上线下结合&…

【论文精读】REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS

REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS 前言ABSTRACT1 INTRODUCTION2 REACT: SYNERGIZING REASONING ACTING3 KNOWLEDGE-INTENSIVE REASONING TASKS3.1 SETUP3.2 METHODS3.3 RESULTS AND OBSERVATIONS 4 DECISION MAKING TASKS5 RELATED WORK6 CONCLUSI…

m_map导入本地地形数据

m_map绘制地形图时&#xff0c;虽然自带有1的地形图以及从NOAA下载的1分的地形图&#xff08;详见&#xff1a;Matlab下地形图绘图包m_map安装与使用&#xff09;&#xff0c;但有时需要对地形图分辨率的要求更高&#xff0c;便无法满足。 此时&#xff0c;需要导入本地地形数…

代理IP和网络加速器的区别有哪些

随着互联网的普及&#xff0c;越来越多的人开始使用网络加速器来提高网络速度。然而&#xff0c;很多人并不清楚代理IP和网络加速器之间的区别。本文将详细介绍两者的概念及区别。 一、代理IP 代理IP是一种通过代理服务器进行网络连接的方式。在使用流冠代理IP时&#xff0c;用…

金南瓜SECS/GEM C# SDK 快速使用指南

本文对如何使用金南瓜SECS/GEM C# SDK 快速创建一个满足SECS/GEM通信要求的应用程序&#xff0c;只需简单3步完成。 第一步&#xff1a;创建C# .NET程序 示例使用Visual Studio 2010&#xff0c;使用者可以选择更高级版本 Visual Studio 第二步&#xff1a;添加DLL库引用&am…

Proteus仿真--基于ADC0808设计的调温报警器

本文介绍基于ADC0808实现的调温报警器设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 温度调节使用滑动变阻器模拟实现&#xff0c;ADC0808采集信号并输出在LCD上面显示&#xff0c;报警系统是LED灯和蜂鸣器实现声光电报警 仿真图如下 仿真运行视频 Proteus仿真…

001 LLM大模型之Transformer 模型

参考《大规模语言模型--从理论到实践》 目录 一、综述 二、Transformer 模型 三、 嵌入表示层&#xff08;位置编码代码&#xff09; 一、综述 语言模型目标是建模自然语言的概率分布&#xff0c;在自然语言处理研究中具有重要的作用&#xff0c;是自然 语言处理基础任务之一…

带大家做一个,易上手的家常土豆丝

搞两个土豆 就能做一盘 然后 去皮 切片 然后再将片切条 然后 绿皮辣椒切片 放一些干辣椒 一些花椒 四五个大料 准备好 然后 起锅烧油 下土豆 翻炒两下 然后放小半碗水 让它煮一下 毕竟土豆不是很好熟的东西 看水少了大半时 放入 干辣椒 绿皮辣椒 大料 花椒 然后 倒入 一滴…

前端知识(九)------------JavaScript底层知识

1.事件循环机制 在实际的编码过程中小伙伴们不知道有没有遇到过这样的问题&#xff0c;我们都知道js是单线程的。而且是一门解释型语言。那么正常来讲执行代码的顺序就是自上而下一句一句执行对吧 但是有的时候我们发现返回的结果并不是自上而下执行的。我们先写了一段代码 …

课堂练习3.3:进程的调度

3-6 课堂练习3.3&#xff1a;进程的调度 在内存中一般存放着数目远大于计算机 CPU 个数的进程&#xff0c;进程调度的作用是选择合适的进程来使用CPU&#xff0c;进程调度器对系统性能有重要影响。本实训分析Linux 0.11的进程调度算法&#xff0c;该操作系统采用了一种时间片与…

redis主从复制【面试必看】

在分布式系统中&#xff0c;希望使用多个服务器来部署redis&#xff0c;存在以下几种redis的部署方式 主从模式主从哨兵集群模式 主从模式 在若干个redis节点中&#xff0c;有的是主节点&#xff0c;有的是从节点 假设有三个物理服务器&#xff08;称为是三个节点&#xff…

MongoDB中的$type操作符和limit与skip方法

本文主要介绍MongoDB中的$type操作符和limit与skip方法。 目录 MongoDB的$type操作符MongoDB的limit方法MongoDB的skip方法 MongoDB的$type操作符 MongoDB中的$type操作符用于检查一个字段的类型是否与指定的类型相匹配。它可以用于查询和投影操作。 $type操作符可以与以下数…

2.2 网络多线程(私聊、群发、发送文件、推送新闻、离线留言)

文章目录 一、私聊1.1 分析1.2 客户端1.2.1 MessageClientService 私聊类1.2.2 ClientConnectServerThread 线程类 1.3 服务端1.3.1 ServerConnectClientThread 线程类 1.4功能演示 二、群发消息2.1 分析2.2 客户端2.2.1 MessageClientService类2.2.2 ClientConnectServerThrea…

★102. 二叉树的层序遍历

102. 二叉树的层序遍历 很巧妙的&#xff0c;又学习了一种层次遍历的方法&#xff0c;就是说根据当前的队列的长度去遍历&#xff0c;遍历的当前队列的长度就是该层次的节点个数。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* Tr…

基于JavaWeb+SSM+Vue童装商城小程序系统的设计和实现

基于JavaWebSSMVue童装商城小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 目 录 摘 要 III Abstract 1 1 系统概述 2 1.1 概述 3 1.2课题意义 4 1.3 主要内容 5…

GoldWave注册机 最新中文汉化破解版-安装使用教程

GoldWave是一个功能强大的数字音乐编辑器&#xff0c;是一个集声音编辑、播放、录制和转换的音频工具。它还可以对音频内容进行转换格式等处理。它体积小巧&#xff0c;功能却无比强大&#xff0c;支持许多格式的音频文件&#xff0c;包括WAV、OGG、VOC、 IFF、AIFF、 AIFC、AU…

Temu卖家如何获取流量?Temu新手卖家流量来源哪里?——站斧浏览器

流量对于每个平台来说都是很重要的&#xff0c;那么Temu卖家如何获取流量&#xff1f;流量来源哪里&#xff1f; Temu卖家如何获取流量&#xff1f; 1、优化产品标题和描述&#xff1a;在Temu平台上&#xff0c;买家通常通过搜索关键词来寻找他们感兴趣的产品。因此&#xff…

curl 18 HTTP/2 stream

cd /Users/haijunyan/Desktop/CustomKit/KeepThreadAlive/KeepThreadAlive //Podfile所在文件夹 git config --global https.postBuffer 10485760000 git config --global http.postBuffer 10485760000 pod install https://blog.csdn.net/weixin_41872403/article/details/86…