【初阶数据结构】二叉树链式结构的实现和遍历

news2024/12/30 2:50:08

 =========================================================================

个人主页

代码仓库

C语言专栏

初阶数据结构专栏

Linux专栏

 =========================================================================

目录

前言

二叉树链式结构的实现

二叉树的遍历

前序、中序和后序遍历

前序遍历

中序遍历

后序遍历

求结点个数

求总的结点个数 

创建变量求结点

创建静态修饰变量

拆分左右子树加根

求叶子节点的个数

求第K层结点个数


前言

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。


二叉树链式结构的实现

我们可以将一个二叉树分成左右两个子树,两个子树又一次分成两个子树,这样一个二叉树就可以被我们拆解左右拆解开来,如果没有左/右孩子记作空。

代码实现

typedef struct BinaryTreeNode 
{
	struct BinaryTreeNode * left;
	struct BinaryTreeNode* right;
	int  val;
}BTNode;
BTNode* BuyBTNode(int x)
{
	BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
	if (tmp == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	tmp->val = x;
	tmp->left = NULL;
	tmp->right = NULL;
	return tmp;
}
int main()
{
	BTNode* node1 = BuyBTNode(1);
	BTNode* node2 = BuyBTNode(2);
	BTNode* node3 = BuyBTNode(3);
	BTNode* node4 = BuyBTNode(4);
	BTNode* node5 = BuyBTNode(5);
	BTNode* node6 = BuyBTNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return 0;
}

创建一个结构体里面有左右两个结构体指针分别指向自己左右孩子 ,像这样上面那个二叉树就使用代码实现了。

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。
再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:
1. 空树
2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。


二叉树的遍历

前序、中序和后序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。 

简单点来说:

前序遍历:先访问根,在访问左子树,最后访问右子树。

中序遍历:先访问左子树,在访问根,最后访问右子树。

后序遍历:先访问左子树,在访问右子树,最后访问根。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

这里的N代表空(NULL)。

前序遍历

二叉树的遍历是使用函数递归实现的,从根开始一次分为左右子树向下遍历左右子树左右子树不存在的话为空(NULL)。

前序遍历图解

 实现代码

void PrevOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	printf("%d ", node->val);
	PrevOrder(node->left);
	PrevOrder(node->right);
}

首先判断根节点是否为空,如果为空则代表空树返回NULL;前序遍历是先访问的根,因此直接打印数据,在依次进入左右子树。这里文字很难讲解清楚,我们直接上函数递归调用图。

中序遍历

这里我就不给大家图解了,大家可以自己尝试画一下。

实现代码 

void InOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	InOrder(node->left);
	printf("%d ", node->val);
	InOrder(node->right);
}

首先判断根节点是否为空,如果为空则代表空树返回NULL;中序遍历是先访问的左子树,因此一直遍历到最后一个根指向的左子树为空时,打印此时根的数据,在依次进入左右子树。

后序遍历

实现代码

void PostOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	PostOrder(node->left);
	PostOrder(node->right);
	printf("%d ", node->val);
}

首先判断根节点是否为空,如果为空代表空树返回NULL;后序遍历也是先访问左子树,因此一直遍历到最后一个根指向的左子树为空时打印此时根的数据,在打印其父亲指向的右子树的数据,最后打印左右子树父亲的数据。


求结点个数

求总的结点个数 

创建变量求结点

很多小伙伴,会想到创建一个变量函数递归依次就代表一个结点,每次递归就加加一次,这样根本

不对,因为变量是开辟在栈区上的函数每次调用都会创建,出函数也都会销毁,这种办法根本行不通。

创建静态修饰变量

使用变量可能会销毁,那我就创建静态变量改变它的生命周期,这样就可以了,求一个二叉树的总节点个数可以,但是我们有多个二叉树呢?也可以解决:在每次求总结点个数前将变量置零就可以了,这是一个可行的办法。

ps:静态变量在函数递归是只会创建一次哦!!!

拆分左右子树加根

二叉树最重要的思想就是拆分二叉树,这里我们将二叉树分成左右子树,使用递归求左右子树的结点个数每次再加上根节点是不是就是总的节点数?

实现代码

int size = 0;
int TreeSize(BTNode* root)
{
	//静态区的变量只初始化一次
	// int size=0;
    //直接使用变量不推荐
	//static int size = 0;
	//if (root == NULL)
	//{
	//	return 0;
	//}
	//else
	//{
	//	++size;
	//}
	//TreeSize(root->left);
	//TreeSize(root->right);
	//return size;

	//优化
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

求叶子节点的个数

情况1:当为空树是叶子节点为空;

情况2:当左右子树为空时叶子结点为1;

情况3:还是使用拆分的方法将一个二叉树拆分,使用递归求解; 

实现代码

int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

求第K层结点个数

假设我们求第三层,还是使用递归拆分树的方法,拆分成左子树的第二层(k-1)加上右子树的第二层(k-1)层。

实现代码

int TreeKlevel(BTNode* root,int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}
	return TreeKlevel(root->left, k - 1) + TreeKlevel(root->right, k - 1);
}

完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct BinaryTreeNode 
{
	struct BinaryTreeNode * left;
	struct BinaryTreeNode* right;
	int  val;
}BTNode;
BTNode* BuyBTNode(int x)
{
	BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
	if (tmp == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	tmp->val = x;
	tmp->left = NULL;
	tmp->right = NULL;
	return tmp;
}
void PrevOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	printf("%d ", node->val);
	PrevOrder(node->left);
	PrevOrder(node->right);
}
void InOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	InOrder(node->left);
	printf("%d ", node->val);
	InOrder(node->right);
}
void PostOrder(BTNode* node)
{
	if (node == NULL)
	{
		printf("NULL ");
		return ;
	}
	PostOrder(node->left);
	PostOrder(node->right);
	printf("%d ", node->val);
}
//求结点个数
int size = 0;
int TreeSize(BTNode* root)
{
	//静态区的变量只初始化一次
	// int size=0;
	//static int size = 0;
	//if (root == NULL)
	//{
	//	return 0;
	//}
	//else
	//{
	//	++size;
	//}
	//TreeSize(root->left);
	//TreeSize(root->right);
	//return size;

	//优化
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//求叶子结点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
//求第K层的结点个数
int TreeKlevel(BTNode* root,int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}
	return TreeKlevel(root->left, k - 1) + TreeKlevel(root->right, k - 1);
}
int main()
{
	BTNode* node1 = BuyBTNode(1);
	BTNode* node2 = BuyBTNode(2);
	BTNode* node3 = BuyBTNode(3);
	BTNode* node4 = BuyBTNode(4);
	BTNode* node5 = BuyBTNode(5);
	BTNode* node6 = BuyBTNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	//前序遍历
	PrevOrder(node1);

	printf("\n");
	//中序遍历
	InOrder(node1);
	printf("\n");
	//后序遍历
	PostOrder(node1);
	printf("\n");
	printf("%d \n", TreeSize(node1));
	printf("%d\n", TreeLeafSize(node1));
	printf("%d\n", TreeKlevel(node1, 3));
	return 0;
}


总结: 二叉树的遍历是用函数递归实现的,确实比较难以理解,我们不仅仅是只做到代码实现层面,还好依据函数递归画出递归的展开图,以便于我们理解和加深印象,这块部分还得多画图。

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

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

相关文章

LeetCode【65. 有效数字】

没有完美的傀儡,没有完美的人类,却有绝美的离逝。 有效数字&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 一个 小数 或者 整数&#xff08;可选&#xff09;一个 e 或 E &#xff0c;后面跟着一个 整数 小数&#xff08;按顺序&#xff09;可以分成以下几…

2023华为杯研究生数学建模竞赛CDEF题思路+模型代码

全程更新华为杯研赛CDEF题思路模型及代码&#xff0c;大家查看文末名片获取 华为杯C题思路分析 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作…

【Java毕设项目】基于SpringBoot+Vue校园便利平台的设计与实现

博主主页&#xff1a;一季春秋博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容&#xff1a;毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

3.6+铁死亡+WGCNA+机器学习

今天给同学们分享一篇3.6铁死亡WGCNA机器学习的生信文章“Identification of ferroptosis related biomarkers and immune infiltration in Parkinsons disease by integrated bioinformatic analysis”&#xff0c;这篇文章于2023年3月14日发表在BMC Med Genomics期刊上&#…

【笔记】ubuntu 20.04 + mongodb 4.4.14定时增量备份脚本

环境 ubuntu 20.04mongodb 4.4.14还没实际使用&#xff08;20230922&#xff09;后续到10月底如果有问题会修改 原理 只会在有新增数据时生成新的备份日期目录备份恢复时&#xff0c;如果恢复的数据库未删除&#xff0c;则会覆盖数据 准备 准备一个文件夹&#xff0c;用于…

2023华为杯数学建模竞赛E题

一、前言 颅内出血&#xff08;ICH&#xff09;是由多种原因引起的颅腔内出血性疾病&#xff0c;既包括自发性出血&#xff0c;又包括创伤导致的继发性出血&#xff0c;诊断与治疗涉及神经外科、神经内科、重症医学科、康复科等多个学科&#xff0c;是临床医师面临的重要挑战。…

Springboot2 Pandas Pyecharts 量子科技专利课程设计大作业

数据集介绍 1.背景 根据《中国科学&#xff1a;信息科学》期刊上的一篇文章&#xff0c;量子通信包括多种协议与应用类型&#xff1a; 基于量子隐形传态与量子存储中继等技术&#xff0c;可实现量子态信息传输&#xff0c;进而构建量子信息网络&#xff0c;已成为当前科研热点&…

成为威胁:网络安全中的动手威胁模拟案例

不断变化的网络威胁形势要求组织为其网络安全团队配备必要的技能来检测、响应和防御恶意攻击。然而&#xff0c;在研究中发现并继续探索的最令人惊讶的事情是&#xff0c;欺骗当前的网络安全防御是多么容易。 防病毒程序建立在庞大的签名数据库之上&#xff0c;只需更改程序内…

聊聊自动化测试路上会遇到的挑战~

一、测试范围 无论是功能测试&#xff0c;还是自动化或者性能测试&#xff0c;第一步要做的&#xff0c;是明确测试范围和需求指标。对于自动化测试来说&#xff0c;特别是UI自动化&#xff0c;并不是所有的功能点都适合做UI自动化。 根据具体的业务情况和项目稳定程度&#…

Unity之Hololens2开发 如何接入的MRTK OpenXR Plugin

一.前言 什么是Hololens? Hololens是由微软开发的一款混合现实头戴式设备,它将虚拟内容与现实世界相结合,为用户提供了沉浸式的AR体验。Hololens通过内置的传感器和摄像头,能够感知用户的环境,并在用户的视野中显示虚拟对象。这使得用户可以与虚拟内容进行互动,将数字信…

conda的安装和使用

参考资料&#xff1a; https://www.bilibili.com/read/cv8956636/?spm_id_from333.999.0.0 https://www.bilibili.com/video/BV1Mv411x775/?spm_id_from333.999.0.0&vd_source98d31d5c9db8c0021988f2c2c25a9620 目录 conda是啥以及作用conda的安装conda的启动conda的配置…

一招解除csdn复制限制

先看这个代码 python读取英文pdf翻译成中文pdf文件导出代码 想要复制代码&#xff0c;csdn有限制怎么办&#xff08;csdn流氓&#xff0c;无耻&#xff09; 解除方法 ctrlu 看效果

PIL或Pillow学习1

PIL&#xff08; Python Imaging Library&#xff09;是 Python 的第三方图像处理库&#xff0c;由于其功能丰富&#xff0c;API 简洁易用&#xff0c;因此深受好评。 自 2011 年以来&#xff0c;由于 PIL 库更新缓慢&#xff0c;目前仅支持 Python 2.7 版本&#xff0c;这明显…

数据仓库数据库

在当今的数字化时代&#xff0c;数据存储和管理是非常重要的领域。数据仓库和数据库是两个重要的数据存储和管理工具&#xff0c;它们有着不同的特点和用途。 一、数据仓库与数据库的定义 1. 数据仓库 数据仓库&#xff0c;是为企业所有级别的决策制定过程&#xff0c;提供所…

2023华为杯D题——基于Kaya模型的碳排放达峰实证研究

一、前言 化石能源是推动现代经济增长的重要生产要素&#xff0c;经济生产活动与碳排放活动密切相关。充分认识经济增长与碳排放之间的关系对转变生产方式&#xff0c;确定碳达峰、碳中和路径极为必要。本研究在对经济增长与碳排放关系现有研究梳理的基础上&#xff0c;系统地分…

postman发送图片

POSTMAN 如何发送携带图片的请求? 闲话不叙 步骤如下&#xff1a; 新建一个请求&#xff0c;在Headers中添加一对k-v : Content-Type > multipart/form-data 请求的接口: RequestMapping("/fileUploadController")public String fileUpload(MultipartFile fil…

手机无人直播手机用哪些软件系统最好?

最近手机无人直播可是风靡大江南北&#xff0c;只要是一个抖音用户都想装个手机无人直播软件&#xff0c;随时随地开启手机无人直播&#xff0c;抖音8亿用户想想这个市场得有多大&#xff0c;蛋糕有多肥。 那么问题来了&#xff0c;手机无人直播手机用啥软件&#xff1f; 推荐…

Learn Prompt-GPT-4:能力

GPT-4能力大赏​ 常识知识推理​ 一个猎人向南走了一英里&#xff0c;向东走了一英里&#xff0c;向北走了一英里&#xff0c;最后回到了起点。他看到了一只熊&#xff0c;于是开枪打了它。这只熊是什么颜色的&#xff1f; 答案是白色&#xff0c;因为这种情况只可能发生在北…

智慧银行:数字化金融时代的引领者

在当今数字化的时代&#xff0c;金融行业正经历着一场前所未有的变革。传统的银行模式已经不再适用&#xff0c;取而代之的是智慧银行的新兴概念。智慧银行不仅仅是数字化的银行&#xff0c;更是一个全新的金融服务范式&#xff0c;将科技与金融相结合&#xff0c;为客户提供更…

第七章 查找

一、树形查找-二叉排序树和红黑树 二叉排序树 // 二叉排序树节点 typedef struct BSTNode{ElemType key;struct BSTNode *lchild, *rchild; } BSTNode, *BSTree;五叉查找树 // 5叉排序树的节点定义 struct Node{ElemType keys[4]; // 5叉查找树一个节点最多4个关键字struct…