梦中情树---二叉树

news2024/10/7 20:29:22

前言:

今天就来讲树的一种特殊结构---二叉树

当然先来给大家看一张图片

 看到这棵树了吗?它从根开始,每个结点都有且仅有两个分支,这个结构就是我们的二叉树。

其实我们上次讲的堆也可以看成一棵二叉树,但是人家的本质是一个数组。

我今天所讲的二叉树就是以链式的结构来展开。

再看链式的二叉树的实现前,还是先来回顾一下二叉树的概念:

1.空树。

2.非空树:有左右两棵子树和根节点所形成。

链式二叉树的基本操作:

1.链式二叉树的结构体:

typedef int BTreeData;
struct BTreeNode
{
	BTreeData data;
	struct BTree* left;
	struct BTree* right;
};

typedef struct BTreeNode BTreeNode;

因为我们讲的二叉树是链式的结构,那么我们的一个根节点除了要包含它本身的数据以外,还需要存储它的两个孩子(左右子树)的指针。

2.创建二叉树的结点:

BTreeNode* BuyNode(BTreeData x)
{
	BTreeNode* node = (BTreeNode*)malloc(sizeof(BTreeNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

其实普通的二叉树的基本操作也就四种:

1.前序遍历

2.中序遍历

3.后序遍历

4.层序遍历

那么我们还是先从第一个开始叭!

3.前序遍历:

前序遍历的顺序:

ex:打印整个二叉树的数据

1.先打印根的数据

2.通过根去访问左子树

3.访问左子树的时候,左子树作为根的时候,打印它的数据,再去访问它的左子树,直到它的左子树为空,再返回

4.返回之后,通过当前的根,去访问这个根的右子树。

5.如果这个右子树作为根时,先打印它的数据,再去访问它的左子树,以此类推。

如果觉得我的表述不准确或难以理解,就来看一下下面的图:

 这个图是否就清晰了不少呢?

那么它的打印顺序就是: A B D E C F G

那么就开始写代码咯~

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%c->", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

如果这个结点是个空结点,那么我们直接返回,当然也可以打印NULL哦,这样更好地去理解遍历的路线。由于我们的前序遍历是:根-左子树-右子树,所以我们就先打印根结点,再去访问左子树,最后去访问右子树。如此去递归。

4.中序遍历:

中序遍历的路线其实和我们的前序遍历的路线是一样的,但是我们的中序遍历的输出时是:左子树-根-右子树。

也就是说,我们要先访问到底部的左子树的左子树为空时,开始对这棵底部的左子树开始打印,一直回溯。

那么还是先来看代码

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePrevOrder(root->left);
	printf("%c->", root->data);
	BinaryTreePrevOrder(root->right);
}

我们会发现这段代码和上面的那段代码就是吧打印的顺序和访问左子树的顺序调换了一下,其实就是这样的,链式二叉树的遍历就是通过递归进行的。

还是看回前面的这张图,打印顺序: D B E A F C G

通过上面的两种遍历我们推出第三种遍历也是极其容易的事情了。

5.后序遍历:

我们就不多说了,直接上代码

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
	printf("%c->", root->data);
}

打印顺序:D E B F G C A

6.层序遍历:

这个遍历就是遍历完一个深度的结点再进入下一层的结点进行遍历,画个简单的图:

就是这么的简单,打印顺序就是 A B C D E F G

但是好像问题来了,不能用简单粗暴的递归完美的把这个遍历很好的解决。

我们来分析一下:

1.先将A遍历

2.再通过A去遍历它的两个孩子B和C

3.再通过B和C去遍历D E F G。

如果像上述去遍历,只能将B和C先存起来,再去按顺序访问B,C并将他们的孩子存到他们的尾部。

我们不可能用栈去存储,因为后面进来的先出去,那么我们只能去选择队列作为存储的容器咯。那么我们还得思考一件事:

我们能一次全存进来,一次性按顺序遍历,梭哈吗?

好像不能哦,我们的队列是一个链式的结构,如果我们让队列将ABCDEFG变成一条单链,他们之间的父子结构就会被破坏了,就无法找到它的左孩子和右孩子了。

那么我们只能先让根进队列,再把左孩子和右孩子入队列,之后马上把这个根给出队列。

那么思路已经有了,那么马上开干!

那么事前还是先把队列的代码给你们叭~

void queueInit(Queue* q) {
	if (q == NULL)
		return;
	q->front = q->rear = NULL;
}
//创建一个新的节点
QNode* creatNode(QDataType data) {
	QNode* node = (QNode*)malloc(sizeof(QNode));
	node->data = data;
	node->next = NULL;
	return node;
}

// 队尾入队列
void queuePush(Queue* q, QDataType data) {
	if (q == NULL)
		return;
	//第一次入队
	if (q->front == NULL) {
		q->front = q->rear = creatNode(data);
	}
	else {
		q->rear->next = creatNode(data);
		q->rear = q->rear->next;
	}
}

// 队头出队列
void queuePop(Queue* q) {
	if (q == NULL || q->front == NULL)
		return;
	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	//若元素出队后,队列为空
	if (q->front == NULL)
		q->rear = NULL;
}

// 获取队列头部元素
QDataType queueFront(Queue* q) {
	return q->front->data;
}
// 获取队列队尾元素
QDataType queueBack(Queue* q) {
	return q->rear->data;
}
// 获取队列中有效元素个数
int queueSize(Queue* q) {
	if (q == NULL)
		return 0;
	int size = 0;
	QNode* node = q->front;
	while (node) {
		size++;
		node = node->next;
	}
	return size;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int queueEmpty(Queue* q) {
	if (q->front == NULL)
		return 1;
	return 0;
}
// 销毁队列
void queueDestroy(Queue* q) {
	QNode* cur = q->front;
	while (cur) {
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = q->rear = NULL;
}

那么队列具备,只欠完成层序遍历的代码啦~

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	queueInit(&q);
	if (root)
		queuePush(&q, root);
	while (!queueEmpty(&q)) {
		//获取队头元素
		BTNode* node = queueFront(&q);
		printf("%c ", node->data);
		//出队
		queuePop(&q);
		//保存队头元素的左右孩子节点
		if (node->left)
			queuePush(&q, node->left);
		if (node->right)
			queuePush(&q, node->right);
	}
	queueDestroy(&q);
	printf("\n");
}

7.二叉树的销毁:

void BinaryTreeDestory(BTNode** root)
{
	if (*root)
	{
		BinaryTreeDestory(&((*root)->left));
		BinaryTreeDestory(&((*root)->right));
		free(*root);
	}
	*root = NULL;
}

养成良好习惯,一定要free掉哦!

那么二叉树就结束啦~下次就是做OJ题了~

不会有人认为已经结束了叭?

下面还有一段呢~

8.二叉树结点的个数:

二叉树的结点个数不就是根本身+ 左子树的结点个数 + 右子树的结点个数吗?

那么我们只需要递归下去即可,那么我们也很容易得知,当一个根为空时,它就可以返回了,而且返回值应该是个0,空嘛~ 他就是一个0

那么直接上代码:

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

9. 二叉树叶子节点个数:

叶子结点的个数 = 左子树的叶子结点的个数 + 右子树的叶子结点的个数

那么什么时候要停下来呢?

如果这个根为空,我们就会返回0,如果这个根不为空,而且这个根的左子树与右子树都是空的,那么这个根就是一个叶子结点,我们就会返回一个1。

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

10.二叉树第k层节点个数:

访问第k层的结点个数,其实就是通过第k-1层的数据将所有数据访问到,依次类推,所以我们要使用层序遍历去获得二叉树的第k层的结点个数。

来看代码:

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

11.二叉树查找值为x的节点:

查找这个值为x的结点,需要我们去从根节点的左子树去找,左子树没有我们要找的结点,我们就去右子树去找。如果我们找到了就返回那个root这个指针,如果到访问到底层的空,则返回NULL指针。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* node = BinaryTreeFind(root->left, x);
	if (node == NULL)
	{
		node = BinaryTreeFind(root->right, x);
	}
	return node;
}

这次就真讲完了!

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

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

相关文章

【python】Jupyter的使用(python代码编辑器)

文章目录一、Jupyter的介绍1、Jupyter是什么?2、Jupyter有什么独特之处?二、Jupyter的安装1、首先要下载python2、用pip命令下载Jupyter三、Jupyter的使用1、运行Jupyter2、简要介绍Jupyter的使用方法3、快捷键的使用四、总结一、Jupyter的介绍 1、Jupy…

安卓系统软键盘初步分析

初步分析的相关日志 复现log: 11-05 14:01:24.768 7991 7991 V InputMethodManager: onViewClicked: true 11-05 14:01:24.768 7991 7991 D InputMethodManager: showSoftInput() viewandroidx.appcompat.widget.AppCompatEditText{bd0acc9 VFED..CL. .F.P..ID 0,0-900,9…

C# 基础:创建、数据类型转换、基本运算符、

VS 快捷键 Ctri K D 代码整理 Ctri KM O 代码收缩 Ctri J 弹出提示 halcon 窗体应用 添加在线第三方库 右键项目名 --> 管理NuGet程序包 halcon 界面,创建选择 添加halcon离线动态链接库 右键引用 --> 添加引用 --> 选择动态链接库 .dll文件 导入…

22勤于思考:gRPC都有哪些优势和不足?

如果你能从专栏的开篇词开始读到这篇文章并且能够在过程中认真思考,那么我相信你目前已经能够对gRPC有了较为充分了解。在专栏的最后几节中,我们抽出一篇文章。来探讨一下gRPC有哪些优势和不足,因为只有这样我们才能取其精华,去其糟粕,学习gRPC框架设计的优点,还能反观出…

NLP领域顶级会议和期刊汇总(附CCF最新推荐目录)

研究NLP需要关注学术界or大厂AI Lab最新的科研动态,了解技术发展的趋势,写论文才能下笔如有神。找到了论文就能找到要复现的代码和要用到的数据集。 掌握科研动态也有助于提早做好产品规划以及技术预研。 对于NLPer而言,了解科研动态最好的方…

总结:网卡

一、背景 经常听到eth0,bond0这些概念,好奇他们的区别,于是有了此篇文章记录下。 二、介绍 网卡:即网络接口板,又称网络适配器或NIC (网络接口控制器),是一块被设计用来允许计算机在计算机网络上进行通讯…

一把LOL的时间我入门了Go语言

走进 Go 语言~ 前言: Go 语言是由 Google 公司推出的一款新的编程语言,作为谷歌的亲儿子,发展势头迅猛,各个大厂目前都在积极推进 Go 语言的使用。Go 是云计算、云原生、区块链等众多前沿领域的首推语言,目前流行的 …

RocketMQ-01

1. MQ介绍 1.1 为什么要用MQ 消息队列是一种“先进先出”的数据结构 其应用场景主要包含以下3个方面 应用解耦 系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任…

Python学习笔记--数据容器

(一) 数据容器入门 1. 数据容器:一种可以容纳多份数据的数据类型,容纳的每一份数据称之为一个元素。每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。 2.. 种类:list(列表…

MongoDB:常见的面试题和答案

1. 什么是MongoDB? MongoDB是一种非关系型数据库,被广泛用于大型数据存储和分布式系统的构建。MongoDB支持的数据模型比传统的关系型数据库更加灵活,支持动态查询和索引,也支持BSON格式的数据存储,这种格式可以支持读…

Go语言流处理,工厂模式,命令参数,序列化,单元测试

IO流 流就是数据在数据源和程序之间经历的路径。数据源可以是文件数据库或者键盘输入等,程序是运行在内存中的应用。 数据从数据源输入到程序的路径为输入流,从内存输出到数据源的路径为输出流。 流是以内存为核心,输入到内存就是输入流&am…

网络原理——IP地址与mac地址

目录 IP地址 IP地址 IP地址的组成 子网掩码 mac地址 冲突域与广播域 数据传输流程 IP地址 IP地址 互联网协议地址。每一个联网的主机都会分配一个IP地址。为32位二进制数,用4个.均分为四部分,在命令提示符中输入:ipconfig命令&#…

35岁的测试工程师被公司强行辞退,感叹道:我以前就该好好努力了

曾经的高薪软件测试工程师,今年35岁了,被公司劝退了,外卖跑到凌晨,很累,但还是有一种想诉说的冲动。哪怕让大家觉得已经说得太多了,烦了,都成祥林嫂了,但是,我是真的想说…

如何报名2023年CDGP数据治理专家认证?看这里

DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…

java中必会String的常用方法(IT枫斗者)

java中必会String的常用方法(IT枫斗者) 概述 在Java语言中,所有类似“ABC”的字面值,都是String类的实例;String类位于java.lang包下,是Java语言的核心类,提供了字符串的比较、查找、截取、大小…

NumPy 初学者指南中文第三版:6~10

原文:NumPy: Beginner’s Guide - Third Edition 协议:CC BY-NC-SA 4.0 译者:飞龙 六、深入探索 NumPy 模块 NumPy 具有许多从其前身 Numeric 继承的模块。 其中一些包具有 SciPy 对应版本,可能具有更完整的功能。 我们将在下一章…

教你用Python和wxPython模块打造一个ChatGPT式打字效果程序

应用场景,可以使用类似ChatGPT回复的打字效果来增强用户体验或提高应用程序的可读性: 聊天机器人:当聊天机器人回复用户消息时,使用打字效果可以更好地模拟真实聊天体验,增强用户对聊天机器人的信任感。电子邮件客户端…

尚硅谷大数据技术Hadoop教程-笔记04【Hadoop-MapReduce】

视频地址:尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优) 尚硅谷大数据技术Hadoop教程-笔记01【大数据概论】尚硅谷大数据技术Hadoop教程-笔记02【Hadoop-入门】尚硅谷大数据技术Hadoop教程-笔记03【Hadoop-HDFS】尚硅谷大数据技术Ha…

DPU02国产USB转UART控制芯片替代CP2102

目录DPU02简介DPU02芯片特性应用DPU02简介 DPU02是高度集成的USB转UART的桥接控制芯片,该芯片为RS-232设计更新为USB设计,并简化PCB组件空间提供了一个简单的解决方案。       DPU02包括了一个USB 2.0全速功能控制器、USB收发器、振荡器、EEPROM和带…

HTML3.1表格

一、表格标签(行列的单元格方式整齐展示数据) 属性值&#xff1a; 标签名说明table 整体&#xff0c;包裹多个tr tr行&#xff0c;包裹tdtd单元格&#xff0c;包裹内容 注&#xff1a;标签嵌套关系&#xff1a;table>tr>td 代码&#xff1a; <!DOCTYPE html> &…