这是一颗经过计划生育的树?

news2024/11/15 8:49:41

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:数据结构中有关"二叉树"的知识,用c语言实现,根据前序遍历构建二叉树,前序遍历,中序遍历,后续遍历,以及层序遍历(有点麻烦)等遍历方式.
金句分享:
✨没人爱时专注自己,有人爱时,有能力拥抱彼此!✨

前言

前面,我们在"树的概念"一文中已经介绍过了二叉树的基本概念,二叉树较于线性表(顺序表和链表等),难度有一定提升,主要是要熟练掌握递归,很多有关"二叉树"的操作都需要使用递归算法.

目录

  • 前言
  • 一、"二叉树"的类型声明
  • 二、"二叉树"的遍历
    • 3.1 前序遍历:
    • 3.2 中序遍历:
    • 3.3 后序遍历:
    • 3.4 扩展知识:层序遍历(稍复杂)
  • 三、"二叉树"的构造(根据前序遍历)
  • 四、"二叉树"的销毁
  • 五、总代码:
    • 声明区(tree.h)
    • 队列接口实现区:(Queue.c)
    • "二叉树"接口实现区:(tree.c)
    • 主测试区:(test.c)

一、"二叉树"的类型声明

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;//数据域
	//指针域
	struct BinaryTreeNode* left;//左子树
	struct BinaryTreeNode* right;//右子树
}BTNode;

二、"二叉树"的遍历

学习二叉树的结构时,最简单的操作是遍历二叉树,所以我们先介绍如何遍历一课二叉树.

二叉树遍历(Traversal):

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

通俗来讲,就是访问一遍二叉树的所有结点.

对于任意一棵二叉树,他都有由,左子树,右子树组成.
那么就出现了三种常见的遍历二叉树的方式

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。即:
根 ---> 左(子树) ---> 右(子树)
  1. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。即:
左(子树) ---> 根 ---> 右(右子树)
  1. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。即:
左(子树) ---> 右(子树) ---> 根

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

总结:

左子树 右子树三个中:

前序遍历:根节点第一个被访问.
中序遍历:根节点第中间(二个)个被访问.
后序遍历:根节点最后一个被访问.

3.1 前序遍历:

看见前序遍历,就知道根节点是第一个被访问的.即:

根 —> 左(子树) —> 右(子树)

代码递归展开图:
在这里插入图片描述

代码实现:

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)//访问到根节点如果是NULL,则打印NULL
	{
		printf("NULL ");
		return;
	}
	//先访问根节点
	printf("%c ", root->data);
	//再递归访问左右子树
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

3.2 中序遍历:

有了前序遍历的基础,后面两个应该好理解,如果还是不理解,可以试着画一下代码的递归展开图,帮助大家理解.

// 二叉树中序遍历 
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//先访问左子树
	BinaryTreePrevOrder(root->left);
	//中间访问根节点
	printf("%c ", root->data);
	//最后访问右子树
	BinaryTreePrevOrder(root->right);
}

3.3 后序遍历:

// 二叉树后序遍历 
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//先访问左右子树
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
	//最后访问根节点
	printf("%c ", root->data);
}

3.4 扩展知识:层序遍历(稍复杂)

要去按层来访问二叉树.

这里需要借助队列来实现,而且恶心的是, C语言没有队列的库函数,需要自己实现.
牛牛有关队列的博客,欢迎直接复制.
传送门

代码实现:
打印NULL版本

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);//将二叉树的根节点先入队列
	while (!QueueEmpty(&q))//只要队列非空则,继续
	{
		BTNode* tmp=QueueFront(&q);
		if (tmp)//非空结点则直接打印数据
		{
			printf("%c ", tmp->data);
		}
		else
		{
			printf("NULL ");
		}
		QueuePop(&q);//弹出根节点.将左右子树分别压入队列
		if (tmp)//只要该结点不是NULL,则将其左右子树都入队
		{
			//结点虽然非空,但是左右子树可能是NULL,所以这里NULL也进入队列了.
			QueuePush(&q, tmp->left);
			QueuePush(&q, tmp->right);
		}
	}
}

不打印NULL版本

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) 
{
	Queue q1;
	QueueInit(&q1);
	QueuePush(&q1, root);//将根节点存入队列
	while (!QueueEmpty(&q1))
	{
		BTNode* front = QueueFront(&q1);//保存队首结点
		printf("%c ",front->data );//打印队首数据
		QueuePop(&q1);//弹出根节点
		//将刚刚弹出的结点的左右孩子入队列(所以前面要保存头结点)
		if (front->left)
			QueuePush(&q1, front->left);
		if(front->right)
			QueuePush(&q1,front->right);
	}
}

三、"二叉树"的构造(根据前序遍历)

前面都是在已经有二叉树的基础上,我们直接遍历二叉树,那二叉树怎么构建呢?

现在,我们给出要构建的二叉树前序遍历.(#代表NULL)

BTDataType arr[50] = { "ABD##E##CF##G##" };

代码实现:

BTNode* BinaryTreeCreate(BTDataType* a,int* pi)//pi用于遍历这个数组
{
	//递归的结束条件是,当left和right都是NULL时,(左右子树都为空,则结束递归)
	if (a[*pi] == '#')//遇到NULL
	{
	//注意,即使是遇到NULL,数组也需要继续往后遍历,不然还没有构建完成
		(*pi)++;
		return NULL;
	}
	//如果不是NULL
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));//创建树结点
	//先赋值根节点
	root->data = a[(*pi)++];
	//再给左右子树赋值
	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a,pi);
	return root;
}

四、"二叉树"的销毁

二叉树的销毁步骤:

  1. 递归访问左右子树,直到遇到NULl.访问到了最后一层.
  2. 开始释放该结点(从叶子结点开始),回退.

在这里插入图片描述

//二叉树的销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)//如果走到NULL则直接返回
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);//这条语句一定要放在前面两条语句的后面,不然无法递归往下走.
}

五、总代码:

声明区(tree.h)

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

typedef char BTDataType;


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

//根据前序遍历构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a,int* pi);

// 二叉树销毁
void BinaryTreeDestory(BTNode* root);

// 二叉树节点个数
int BinaryTreeSize(BTNode* root);

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);



//队列
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef BTNode* QDatatype;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDatatype x);
void QueuePop(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
QDatatype QueueFront(Queue* pq);
QDatatype QueueBack(Queue* pq);

队列接口实现区:(Queue.c)

#include "tree.h"
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	QNode* next = cur;
	while (next)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDatatype x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->data = x;
	newnode->next = NULL;
	if (newnode == NULL)
	{
		perror("newnode malloc fail:");
		return;
	}
	//这里忘记了判断head刚开始时
	if (pq->head == NULL)//第一次插入
	{
		assert(pq->tail == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;//记住这个放后面
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	if (pq->head == pq->tail && pq->head == NULL)
	{
		return true;
	}
	return false;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL)//代表还剩下一个结点
	{
		free(pq->head);//释放这个结点.
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}

QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->data;
}

"二叉树"接口实现区:(tree.c)

#include "tree.h"

BTNode* BinaryTreeCreate(BTDataType* a,int* pi)//pi用于遍历这个数组
{
	//递归的结束条件是,当left和right都是NULL时
	if (a[*pi] == '#')//遇到NULL
	{
		(*pi)++;
		return NULL;
	}
	//如果不是NULL
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));//创建树结点
	root->data = a[(*pi)++];
	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a,pi);
	return root;
}


//二叉树的销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)//如果走到NULL则直接返回
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);//这条语句一定要放在前面两条语句的后面,不然无法递归往下走.
}

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}


// 二叉树中序遍历 
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreePrevOrder(root->left);
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->right);
}

// 二叉树后序遍历 
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
	printf("%c ", root->data);
}


// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);//将二叉树的根节点先入队列
	while (!QueueEmpty(&q))//只要队列非空则,继续
	{
		BTNode* tmp=QueueFront(&q);
		if (tmp)
		{
			printf("%c ", tmp->data);
		}
		else
		{
			printf("NULL ");
		}
		QueuePop(&q);//弹出根节点.将左右子树分别压入队列
		if (tmp)
		{
			QueuePush(&q, tmp->left);
			QueuePush(&q, tmp->right);
		}
	}
}

主测试区:(test.c)

#include "tree.h"

int main()
{
	BTDataType arr[50] = { "ABD##E##CF##G##" };
	int i = 0;
	BTNode* root = BinaryTreeCreate(arr,&i);

	//前序遍历
	printf("前序遍历:");
	BinaryTreePrevOrder(root);
	printf("\n");

	// 二叉树中序遍历 
	printf("中序遍历:");
	BinaryTreeInOrder(root);
	printf("\n");

	// 二叉树后序遍历 
	printf("后序遍历:");
	BinaryTreePostOrder(root);
	printf("\n");

	//层序遍历
	printf("二叉树的层序遍历:");
	BinaryTreeLevelOrder(root);
	printf("\n");

	//二叉树的销毁
	BinaryTreeDestory(root);
	return 0;
}

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

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

相关文章

Hadoop的shuffle过程及调优

MapReduce 中的Shuffle 发生在 map 输出到 reduce 输入的过程&#xff0c;它的中文解释是 “洗牌”&#xff0c;顾名思义该过程涉及数据的重新分配&#xff0c;主要分为两部分&#xff1a; map 任务输出的数据分组、排序&#xff0c;写入本地磁盘。reduce 任务拉取排序。 由于…

基于Java+Swing+Mysql实现人事管理信息系统

基于JavaSwingMysql实现人事管理信息系统 一、系统介绍二、功能展示1.用户登陆2.用户注册3.员工信息添加、删除4.员工信息查询、修改5.部门管理6、员工考核 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 系统功能&#xff1a;用户登陆、用户注册、员工信息添加、…

6.8object类equals tostring

2 什么是API API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件可以访问的一些功能集&#xff0c;但又无需访问源码或理解内部工作机制的细节. API是一种通用功能集,有时公…

基于Java+Swing+Mysql实现旅游管理信息系统

基于JavaSwingMysql实现旅游管理信息系统 一、系统介绍二、功能展示1.登陆2.注册3.旅游信息查询4.查看游行团信息5.报名6、报名信息管理 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 用户&#xff1a;登陆、注册、旅游信息查询、查看游行团信息、报名 管理员&a…

【ARIMA-SSA-LSTM】合差分自回归移动平均方法-麻雀优化-长短期记忆神经网络研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

数据库约束、表的关系

数据库约束、表的关系 数据库约束、表的关系 1. 数据库约束1.1 约束类型1.2 NULL约束1.3 UNIQUE&#xff1a;唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束1.6 FOREIGN KEY&#xff1a;外键约束 2. 表的设计2.1 一对一2.2 一对多2.3 多对多 …

ODOO16 ERP如何做标准的研发费用归集?

目前国家大力鼓励企业研发投入&#xff0c;并且给予很多鼓励。如《中共中央关于制定国民经济和社会发展第十四个五年规划和二〇三五年远景目标的建议》中明确提出&#xff1a;“发挥企业家在技术创新中的重要作用&#xff0c;鼓励企业加大研发投入&#xff0c;对企业投入基础研…

2023洗发护发市场分析(头皮清洁护理等新兴品类销售数据分析)

如今&#xff0c;随着人们消费观念的转变&#xff0c;对洗发护发相关用品的要求也逐渐提高&#xff0c;由原来单一的清洁功能到更注重洗发护发用品的功能及护理效果。因此&#xff0c;洗发护发产品的品类不断增加&#xff0c;洗发护发产品的市场规模也不断扩大&#xff0c;整体…

JAVA实现一个工作流引擎

介绍 工作流是一种将一系列相关的任务和活动组织起来的技术&#xff0c;以便在企业或组织中自动化或半自动化地管理业务流程。工作流技术可以帮助企业或组织更好地管理和优化业务流程&#xff0c;提高生产效率和质量&#xff0c;降低成本和风险。 JAVA作为一种面向对象编程语…

线程池与CompletableFuture 异步编排

使用线程池的好处&#xff1a; 1、降低资源的消耗 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗 2、提高响应速度 因为线程池中的线程数没有超过线程池的最大上限时&#xff0c;有的线程处于等待分配任务 的状态&#xff0c;当任务来时无需创建新的线程就能执行…

STM32启动详细流程分析(一)

问题提出 大家不妨设想一下&#xff0c;cpu 的工作是什么&#xff0c;cpu 是没有主观意识的&#xff0c;它只会按照特定的指令执行相应的操作&#xff0c;用专业术语来说就是&#xff1a;取指 -> 译码 -> 执行&#xff0c;译码和执行肯定是在 cpu 内部进行操作的&#x…

MySQL数据库增删改查及聚合查询SQL语句学习汇总

目录 数据库增删改查SQL语句 MySQL数据库指令 1.查询数据库 2.创建数据库 3.删除数据库 4.选择数据库 创建表table 查看所有表 创建表 查看指定表的结构 删除表 数据库命令进行注释 增删改查&#xff08;CRUD&#xff09;详细说明 增加 SQL库提供了关于时间的…

Python 查询 DynamoDB

文章目录 DynamoDB 简介Boto3简介安装和导入 Boto3将 Boto3 连接到 DynamoDB 使用 Boto3 在 DynamoDB 中创建表通过 Boto3 删除 DynamoDB 中的表通过 Boto3 列出 DynamoDB 中的表通过 Boto3 在 DynamoDB 中分页通过 Boto3 在 DynamoDB 中排序通过 Boto3 在 DynamoDB 中获取项目…

【设计模式与范式:行为型】63 | 职责链模式(下):框架中常用的过滤器、拦截器是如何实现的?

上一节课&#xff0c;我们学习职责链模式的原理与实现&#xff0c;并且通过一个敏感词过滤框架的例子&#xff0c;展示了职责链模式的设计意图。本质上来说&#xff0c;它跟大部分设计模式一样&#xff0c;都是为了解耦代码&#xff0c;应对代码的复杂性&#xff0c;让代码满足…

Ribbon LoadBalanced底层机制源码探秘

&#x1f34a; Java学习&#xff1a;社区快速通道 &#x1f34a; 深入浅出RocketMQ设计思想&#xff1a;深入浅出RocketMQ设计思想 &#x1f34a; 绝对不一样的职场干货&#xff1a;大厂最佳实践经验指南 &#x1f4c6; 最近更新&#xff1a;2023年6月18日 &#x1f34a; 点…

如何自己开发浏览器js插件

大家都知道在网页控制台编写的js脚本一刷新就没了下面教程教大家如何自己写一个js插件&#xff0c;此教程是小白在网上看到的的确认有效才发出来的&#xff0c;无需借助油猴。 最近工作需要小白研究了一下浏览器插件编写的过程下面分享给大家 步骤 在桌面创建一个文件夹&…

指针与数组---指针与一维数组的关系

C语言的高效得益于它指针功能的强大。然而C语言中的指针和数组的关系似乎很“纠结”&#xff0c;让人爱恨交织。指向数组的指针变量、指针数组等&#xff0c;似乎总是“你中有我&#xff0c;我中有你”。 目录 一、数组名的特殊意义及其在访问数组元素中的作用 二、指针运算…

Linux常用指令和知识(1)

目录 ls cd pwd 相对路径&绝对路径&特殊路径符 mkdir touch-cat-more cp-mv-rm which-find grep-wc 管道符 | echo 重定向符 tail &#x1f636;‍&#x1f32b;️&#x1f618;创作不易, 多多支持 前言: 我们学习的Linux命令, 其实他们的本体就是一个个…

ctf 逆向 专题题解

本文的目标是&#xff0c;记录一些不具备通用性的&#xff0c;或者比较进阶的题目。之前的另一篇文章则用于记录一些基础知识和通用性较强的基本手法。 文章目录 跨科题目buu fungame&#xff1a;reverse与pwn的结合reverseweb 反跟踪Easyhook&#xff1a;hook例题 vm类型总结一…

我的创作纪念日——512

机缘 没想到不知不觉在CSDN创作就512天了&#xff0c;想到一开始就仅仅想在CSDN记笔记&#xff0c;到现在成为一个小博主&#xff0c;认识到了很多志同道合的伙伴&#xff0c;中间创作我也曾经懒惰过&#xff0c;放弃过&#xff0c;但我一次又一次重新进行创作&#xff0c;虽然…