数据结构-二叉树的代码实现(详解)

news2024/11/26 7:44:29

内容:二叉树的前、中,后序遍历,层序遍历,二叉树节点个数,叶子节点个数,二叉树高度,第k层节点的个数,查找某个节点,二叉树销毁,判断是否为完全二叉树

目录

 前序遍历:

中序遍历:

后序遍历:

层次遍历:需要借助队列

 二叉树节点个数:

 二叉树叶子节点的个数:

二叉树的高度:

二叉树第k层的节点个数:

查找某个节点并返回其地址:

二叉树销毁:

判断是否为完全二叉树:借助队列


事前准备:

typedef int BTDataType;
typedef struct BinaryTreeNode//二叉树节点
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode*right;
}BTNode;

BTNode* BuyNode(BTDataType x)//创建一个节点
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

手动构造一个二叉树,用以验证:图示如下

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);

	node1->left = node2;
	node1->right = node3;
	node2->left = node4;

	return node1;
}

 前序遍历:

void PrevOrder(BTNode* root)
{
	if (root == NULL)//进入一个空树
	{
		printf("N ");
		return;
	}

	printf("%d ", root->data);//访问根节点的值
	PrevOrder(root->left);//访问左子树
	PrevOrder(root->right);//访问右子树
}

  

中序遍历:

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	InOrder(root->left);//访问左子树
	printf("%d ", root->data);//访问根节点
	InOrder(root->right);//访问右子树
}

 

后序遍历:

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	PostOrder(root->left);//访问左子树
	PostOrder(root->right);//访问右子树
	printf("%d ", root->data);//访问根节点
}

层次遍历:需要借助队列

借助队列先进先出的性质,一个节点出队,带入它不为空的左右孩子入队

相关队列功能的所需函数及队列结构的定义:

typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode//队列中的节点
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue//队列
{
	QNode* phead;//指向队头的指针
	QNode* ptail;//指向队尾的指针
	int size;
}Queue;

void QueueInit(Queue* pq);//初始化
void QueueDestroy(Queue* pq);//销毁
void QueuePush(Queue* pq, QDataType x);//插入
void QueuePop(Queue* pq);//删除
QDataType QueueFront(Queue* pq);//获取队头元素
bool QueueEmpty(Queue* pq);//判空

 

void QueueInit(Queue* pq)
{
	assert(pq);//断言,pq一定不为空
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)//第一次插入,链表中没有节点
	{
		assert(pq->phead == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else//尾插
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->phead->next == NULL)//链表中仅有一个节点
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else//多个节点,头删
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}


bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

层序遍历:

void LeverOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)//将根节点入队
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))//一个节点出队,带入它的左右孩子
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);

		if (front->left)//左孩子不为空,入队
		{
			QueuePush(&q, front->left);
		}

		if (front->right)//右孩子不为空,入队
		{
			QueuePush(&q, front->right);
		}
	}

	printf("\n");
	QueueDestroy(&q);
}

 二叉树节点个数:

空树返回0 

非空树返回:左子树节点的个数+右子树节点的个数+1

int BTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BTreeSize(root->left)
		                    + BTreeSize(root->right) + 1;
}

 二叉树叶子节点的个数:

进入一个空树返回0

非空树:若本身是叶子,返回1,否则返回左子树叶子的个数+右子树叶子的个数

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

	if (root->left == NULL && root->right == NULL)//本身是叶子
	{
		return 1;
	}

	return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);//非叶子
}

二叉树的高度:

 进入一个空树,返回0

非空树:找出左右子树高度更高的一个,返回其+1

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

	int leftHeight = BTreeHeight(root->left);
	int rightHeight = BTreeHeight(root->right);

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

二叉树第k层的节点个数:

 进入空树,返回0

非空树:若它的k==1,则求的就是它这一层,返回1

              否则,返回它左子树第k-1层的节点个数+右子树第k-1层的节点个数

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

	if (k == 1)
	{
		return 1;
	}

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

如求其第2层的节点个数:

查找某个节点并返回其地址

BTNode* BTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)//根节点就是,直接返回
	{
		return root;
	}

	BTNode* ret1 = BTreeFind(root->left, x);//去左子树找
	if (ret1)//找到了,返回
	{
		return ret1;
	}

	BTNode* ret2 = BTreeFind(root->right, x);//去右子树找
	if (ret2)//找到了,返回
	{
		return ret2;
	}

	return NULL;//根,左子树,右子树全都找不到,返回NULL
}

二叉树销毁:

void BTreeDestroy(BTNode* root)
{
	if (root == NULL)//进入空树,返回
	{
		return;
	}

	BTreeDestroy(root->left);//销毁左子树
	BTreeDestroy(root->right);//销毁右子树
	free(root);//销毁根节点
}

判断是否为完全二叉树:借助队列

一个节点出队,让它的左右孩子进队,不管左右孩子是否为空

若是完全·二叉树,则出到第一个NULL时,这个NULL后面的所有队列元素都是NULL

若不是完全二叉树,则出到第一个NULL时,这个NULL后面的所有队列元素中有元素不是NULL

bool BTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)//根节点入队
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))//出队列元素
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)//出到第一个NULL,结束循环,进行判断它后面的所有处在队列中的元素是否都为NULL
		{
			break;
		}

		QueuePush(&q, front->left);//不管是否为空,左右孩子都进队
		QueuePush(&q, front->right);
	}

	while (!QueueEmpty(&q))//判断第一个NULL出队后,队中所有元素的情况
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front)//如果队中有非空节点,则不是完全二叉树
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;//第一个NULL出队后,队中所有元素均为NULL
}

完结撒花~ 

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

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

相关文章

科研热点|《电气工程领域高质量科技期刊分级目录(2022版)》公示!

为贯彻落实《关于深化改革 培育世界一流科技期刊的意见》,推进国内外科技期刊的同质等效使用,助力我国科技期刊高质量发展,在中国科协的统一部署下,中国电工技术学会制定《电气工程领域高质量科技期刊分级目录发布工作实施方案&am…

【HarmonyOS】ArkTS学习之页面转场动画的实现

【关键字】 ArkTS、转场动画、PageTransitionEnter、PageTransitionExit 【介绍】 动画交互的实现是一种提升用户体验的方式。动画分为属性属性动画、显示动画、转场动画和路径动画,而转场动画又包含页面间转场、组件内转场和共享元素转场。我觉得页面转场动画挺有趣…

电机的调压调速和PWM调速方法的性能比较、应用场景简介

电机的调压调速和PWM调速是两种不同的电机控制方式。 调压调速:调压调速是通过改变电机输入电压的大小来控制电机的转速和扭矩。通过调节电压,可以改变电机的转矩特性和转速。这种调速方式适用于直流电机和某些类型的交流电机,如感应电动机。…

Windows电源模式(命令行)

一、简介 windows使用powercfg.exe来控制电源方案,像cmd.exe一样,powercfg.exe也是windows自带的。 powercfg命令行选项 选项说明/?、-help显示有关命令行参数的信息。/list、/L列出所有电源方案。/query、/Q显示电源方案的内容。

华为云CodeArts DevSecOps系列插件——助力更高效的软件研发

HDC期间入驻华为云,可参与Toolkit插件抽奖活动,活动链接在文末 一、前言 DevOps的概念想必大家都不陌生,它是一组过程、方法与系统的统称,通过它可以对交付速率、协作效率、部署频率速率、质量、安全和可靠性等进行提升改善。相比…

Linux——信号发送代码

目录 一.信号集操作函数 sigpromask函数 代码详解: ​编辑 考虑情况一:对多个信号的屏蔽 考虑情况二:取消对信号的阻塞 运行结果: 考虑情况三:若不想让进程退出,采用自定义捕捉信号方式!…

Java阶段五Day02

Java阶段五Day02 文章目录 Java阶段五Day02MAVEN-聚合(多模块3)回顾多模块2个特性依赖:继承: 聚合场景聚合目的实现聚合聚合总结 远程仓库远程仓库概念配置settings.xml远程库配置注意事项 GIT详细学习git概括git历史本地版本控制相关命令git分支管理分支管理基本概念分支管理相…

axios请求错误处理Uncaught runtime errors:handleError@webpack-internal:///./node_modules/webpack-dev-server

项目环境: vue3.2.13 vue-cli5.0.0 注:该问题只是出现在开发环境,打包后是不会出现的。 问题描述: 由于需要新开一个开发项目,引入必备网络请求axios及他的副手(集中处理请求及返回数据)&a…

springboot+MySQL大学生体质测试管理系统

功能需求分析的任务是通过详细调查大学生体质测试的测试信息管理系统要处理的所有对象,通过充分了解大学生体质测试管理系统的工作流程,明确使用者的各种需求,充分思考之后可能扩充和改变的情况,然后在这个基础上来设计数据库。

C#(五十九)之三种数据结构 stack queue sortedList

堆栈(Stack): 代表了一个后进先出的对象集合。当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。 Stack 类的方法和…

UVM中transaction中数据持续时间的控制

一、代码 首先需要确认持续时间是在driver中进行控制 其中data_size就是数据的持续时钟周期个数

如何看待中国工业MES系统的发展现状?

如何看待中国工业MES系统的发展现状? MES系统在中国工厂推进是否举步维艰,MES系统是否多数华而不实?中国工业化的前途在何方? 什么是MES—— 制造执行系统(MES)一种用于监控、控制和优化制造过程的软件系统…

多元回归预测 | Matlab基于粒子群算法(PSO)优化高斯过程回归(PSO-GPR)的数据回归预测,matlab代码,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于粒子群算法(PSO)优化高斯过程回归(PSO-GPR)的数据回归预测,matlab代码,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分…

【node】借助百度智能云,识别并提取图片中的文字

效果图: 一、对接百度智能云 登录百度智能云:百度智能云-云智一体深入产业 新用户可以免费体验,按照下面来就行: 创建应用之后就会有密钥啥的了 二、在node项目中安装依赖并使用 所有依赖如下: "baidu-aip-sdk…

Python中的*args和**kwargs:无限可能的函数参数详解

概要 在 Python 编程中,*args 和 **kwargs 是常用的两个特殊参数,用于处理不确定数量的函数参数。它们提供了一种灵活的方式来传递和接收参数,使函数的定义更加通用。本文将详细介绍 *args 和 **kwargs 的使用方法,并提供相关代码…

UnityVR--机械臂场景9-简单流水线应用1

目录 一. 整体介绍 二. 准备工作 三. 建立流水线 1. 流水线包含的功能: 2. 流水线的代码结构 3. 场景实现 4. 测试运行 一. 整体介绍 流水线是在空间和时间上合理安排和组织工艺线路的一种形式,它涉及到一种或多种生产设备、传感器、物料传输设备等&…

企业微信群机器人开发

准备工作 已经注册了有效的企业微信账号,并且在客户端上已经登录现有或者新建有效的包含多名企业微信成员的群聊 创建群聊机器人 右键群聊->管理聊天信息->添加群机器人 使用群机器人 在终端某个群组添加机器人之后,创建者可以在机器人详情页…

【Unity天空盒的制作及使用】使用HDR图或六面图制作天空盒及天空盒的几种使用方式

前言【Unity天空盒的制作及使用】使用HDR图或六面图制作天空盒及天空盒的几种使用方式一 、制作天空盒材质1.1 使用HDR文件1.2 使用六张图片二、天空盒的使用方式2.1 场景中设置天空盒2.2 相机中设置天空盒总结前言 Unity工程中每次打开工程映入眼帘的蓝灰过渡的画面就是Unity默…

随笔 --- 是否可以用左右子树结点数差来衡量二叉树是否平衡?

这道题目来自mooc上浙江大学由陈越 、何钦铭老师主讲的数据结构课程上。 题目如下: 由于评论区关闭,在这里写下我自己的思路: 根据要求使用结点数来衡量是否是平衡时,极端条件是NR 2NL 或者反之。 如果构建二叉树时恰好构建为斜…

xxl-job使用详解(包含部分踩坑信息)

概念 XXL-JOB 是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 分布式下,每个服务都可以搭建为集群,这样的好处是可以将任务切片分给每一个服务从而实现并行执行,提高任务调度的处理效率。 在…