C数据结构:二叉树

news2024/9/20 17:28:42

目录

二叉树的数据结构

前序遍历

中序遍历

后序遍历

二叉树的创建

二叉树的销毁

二叉树的节点个数

二叉树叶子节点个数

二叉树第K层节点个数

二叉树的查找

层序遍历

判断二叉树是否为完全二叉树

完整代码


二叉树的数据结构

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

这里二叉树是使用了链式存储

data负责存储数据,left指针负责存储左孩子节点,right负责存储右孩子节点

前序遍历

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

	printf("%d ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

前序遍历的规则是先访问根节点,然后访问左子树,最后访问右子树

根据规则我们在遍历前先打印当前根节点的值(若是遇到NULL则打印N代替空节点),然后再往下递归左子树、右子树即可 

中序遍历

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

	BinaryTreeInOrder(root->_left);
	printf("%d ", root->_data);
	BinaryTreeInOrder(root->_right);
}

中序遍历的规则是先访问左子树,然后访问根节点,最后访问右子树

 所以这里先递归到左子树(最左边),然后打印当前节点的值,然后再访问右子树(右子树的左子树),打印值,循环往复

后序遍历

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

	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%d ", root->_data);
}

 后序遍历的规则是先访问左子树,然后访问右子树,最后访问根节点

跟前面的前序遍历和中序遍历基本一致,都只是顺序不一样

二叉树的创建

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (*pi >= n)
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->_data = a[(*pi)++];
	root->_left = BinaryTreeCreate(a, n, pi);
	root->_right = BinaryTreeCreate(a, n, pi);
	return root;
}

使用该函数需要传一个数组,并根据数组里的元素当作前序遍历构建起二叉树

并需要传一个数组的大小,和一个标记pi,在外面传参的时候从数组开始的下标开始传

这里为了让函数在递归过程中能够始终有一个变量能够指向数组的某个元素来迭代(遍历数组),不受递归变化而变化,所以需要一个指针变量,在下一次递归的时候只需要传地址,就能获得*pi的值,而不是通过传参

 在每次递归过程中需要先创建一个根节点root并开辟一块空间,并为该节点data赋值为当前走到的*pi的位置

接下来就要开始递归先从根的左子树开始到右子树,并分别赋值给root的左和右

这里的递归会一直递归,我们需要让他递归到空为止,所以需要设定if语句限定递归次数

if语句限定了当*pi超过了数组的长度或等于数组长度时,则不能继续递归获取空间

所以这里是从最后一个节点开始往回返,先return最后一个节点给上一个递归的左或者右节点,依次往复即可

二叉树的销毁

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;

	BinaryTreeDestory(root);
	BinaryTreeDestory(root);
	free(root);
}

销毁我们需要用后序遍历,因为前序和中序会先将根节点给销毁了,这样就不好往下找了,故此要使用后序遍历来一个个free掉每一个节点

二叉树的节点个数

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

	return BinaryTreeSize(root->_left)
		+ BinaryTreeSize(root->_right) + 1;
}

控制好递归的次数 

只需要在遍历每一层节点时+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);
}

还是先控制递归的层数,当root等于空时往回返

若root的左和右都为空,则说明当前节点为叶子节点,返回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);
}

 和前面的递归思路都差不多

这里返回1的条件是k == 1,因为只需要递归k-1次即可,当递归次数达到说明也到了第k层,则返回1即可

最后返回给外层的时候把左右子树的结果返回起来相加即可

二叉树的查找

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

	if (root->_data == x)
		return root;

	BTNode* ret1 = BinaryTreeFind(root->_left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->_right, x);
	if (ret2)
		return ret2;

	return NULL;
}

 第一个if语句控制层数

第二个判断查找结果,若找到则返回根节点root

找到的结果返回给下面的ret1指针或者ret2指针,通过这两个指针返回给最外层

层序遍历

void BinaryTreeLevelOrder(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);
	}
	QueueDestroy(&q);
}

层序遍历是一层一层从左到右遍历,那么我们就需要借助队列来完成(先进先出)

例如该图层序遍历后就是1,2,4,3,5,6

首先我们需要将根节点入队列

然后进入循环,我们循环的条件是队列为空则停止循环

所以我们还需要不断入栈

这里第一层已经入栈了,这时候就可以拿到这个节点,我把它叫做front

后面我们就可以打印这个front节点的data

然后就可以删除掉这个节点了,但是删除的同时需要把它的左右孩子带进来,但是左右孩子有可能为空,所以先判断,若不为空则push入队列,这样第二层就入队列了

接下来继续pop2和4(pop的同时打印),然后push2和4的左右孩子

 最后不要忘记将队列销毁,防止造成内存泄漏

判断二叉树是否为完全二叉树

int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
			break;

		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			QueueDestroy(&q);
			return 0;
		}
	}
	QueueDestroy(&q);
	return 1;
}

和前面层序遍历差不多,我们也需要借用一个队列来完成该函数

先入第一个根节点进队列,然后循环的将二叉树的全部节点按照层序遍历都push进去 

push到最后队列的头节点front总会碰到空节点,所以我们碰到空节点就break出去循环,这是第一个循环的主要作用

例如该二叉树,我们push2的时候就会把左节点3和右节点NULL给入队列,那么我们的front肯定会接收到NULL从而跳出循环

如上并不是一个完全二叉树,所以我们判断它不是完全二叉树的理由就是根据层序遍历它的空节点后面还有一个节点6

所以我们第二个循环的任务就是pop掉队列剩下的所有值,若pop的时候发现里面还有非空节点,则不为完全二叉树,return 0

例如我们在把NULL节点接收给front时,我们肯定会经过4这个节点,然后4这个节点就会把它的左NULL和右6两个节点入队列

所以我们最后在把队列全部出掉的时候肯定会碰到这个6,既然碰到了这个6就说明这不是一个完全二叉树

完整代码

#include"BinaryTree.h"
#include"Queue.h"

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (*pi >= n)
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->_data = a[(*pi)++];
	root->_left = BinaryTreeCreate(a, n, pi);
	root->_right = BinaryTreeCreate(a, n, pi);
	return root;
}

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;

	BinaryTreeDestory(root);
	BinaryTreeDestory(root);
	free(root);
}

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

	return BinaryTreeSize(root->_left)
		+ BinaryTreeSize(root->_right) + 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);
}

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);
}

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

	if (root->_data == x)
		return root;

	BTNode* ret1 = BinaryTreeFind(root->_left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->_right, x);
	if (ret2)
		return ret2;

	return NULL;
}

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

	printf("%d ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

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

	BinaryTreeInOrder(root->_left);
	printf("%d ", root->_data);
	BinaryTreeInOrder(root->_right);
}

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

	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%d ", root->_data);
}

void BinaryTreeLevelOrder(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);
	}
	QueueDestroy(&q);
}

int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
			break;

		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			QueueDestroy(&q);
			return 0;
		}
	}
	QueueDestroy(&q);
	return 1;
}

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

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

相关文章

Golang的内存关系

1.Page Golang的Page,在操作系统对虚拟内存管理的MMU定义的物理页有相似的定义,默认的Page为8KB 2.mSpan 多个连续的Page称之为是一个Span,其定义含义有操作系统的管理的页表相似 3.Size Class Size Class: 相当于 一个等级和刻度, 比如 第二等级 就代表 一个Pag…

【C++ ——— 多态】笔记

文章目录 一、多态概念二、多态的定义即实现2.1 多态的构成条件2.2 虚函数2.3虚函数的重写1.虚函数中析构函数的重写2.重载、重写(覆盖)、重定义(隐藏)的区别 2.4 C11 override 和 final 三、抽象类3.1抽象类概念3.2 接口继承和实…

中断处理过程介绍

概念 中断 中断源 分类 中断处理过程 中断请求 实现器件 中断判优 软件判优 过程 器件实现 程序实现 硬件判优 链路判优 器件实现 控制器判优 中断响应 中断服务 中断返回

C语言作为计算机行业的基础之一,是否制约了行业本身的发展?

c不是计算机行业的基础啦,你想,c语言出现时已经有一套成熟的计算机体系,有基于内存地址的寻找指令、数据的工作方式,有汇编语言,那搞出c这种高级语言就很正常啊!刚好我有一些资料,是我根据网友给…

代码随想录算法训练营第20天 |● 654.最大二叉树 ● 617.合并二叉树 ● 700.二叉搜索树中的搜索 ● 98.验证二叉搜索树

文章目录 前言654.最大二叉树思路方法一 递归法方法一2 老师的优化递归法 617.合并二叉树思路方法一 递归法方法二 迭代法 700.二叉搜索树中的搜索思路方法一 递归法方法二 迭代法 98.验证二叉搜索树思路方法一 使用数组方法二 不使用数组代码注意点: 方法二 使用双…

【Linux】Linux的基本指令_3

文章目录 二、基本指令15. date16. cal16. find17. grep18. zip 和 unzip19. tar20. uname 未完待续 二、基本指令 15. date date 命令可以显示当前时间。 常用标记列表: %H : 小时(00…23) %M : 分钟(00…59) %S : 秒(00…61) %X : 相当于 %H:%M:%S %d : 日 (01……

简易计算器

前言 简易计算器,旨在实现一个简单的计算器功能。 整形,浮点型数据的加减乘除运算;数据的统计(如文件中某字符的出现频数);期望,方程运算;平均数,最小值,最大值,中位数…

C++之“流”-第2课-C++和C标准输入输出同步

为什么C和C的标准输入输出不同步时,数据会混乱?同步会带来多大性能损失?为什么说这个损失通常不用太在乎? 0. 课堂视频 C之“流”-第2课:和C输入输出的同步 1. 理解cin和cout的类型与创建过程 std::cout 是std::ostre…

Css 提高 - 获取DOM元素

目录 1、根据选择器来获取DOM元素 2.、根据选择器来获取DOM元素伪数组 3、根据id获取一个元素 4、通过标签类型名获取所有该标签的元素 5、通过类名获取元素 目标:能查找/获取DOM对象 1、根据选择器来获取DOM元素 语法: document.querySelector(css选择…

C/C++运行时库和UCRT系统通用运行时库总结及问题实例分享

目录 1、概述 2、不同版本的Visual Studio对应的运行时库说明 3、在Windbg10.0安装目录中获取UCRT通用运行时库 4、微软官网对UCRT通用运行时库的相关说明 5、使用Visual Studio 2017开发软件初期遇到的UCRT通用运行时库问题 6、如何查看软件依赖了哪些C/C运行时库&#…

vueRouter路由总结

https://blog.csdn.net/qq_24767091/article/details/119326884

奇门遁甲古籍《烟奇要览》

《烟奇要览》 全书共178页 时间有限,仅上传部分图片!

js深入理解对象的 属性(properties)的特殊 特性(attributes)

对象 js对象 // 构造一个对象 let obj {}; let obj new Object(); 我们知道js中一切皆对象,对象是一个键值对集合(key: value),一个键(key)对应一个值(value),而每个键都是这个对象的属性,我们可以通过对象的属性来…

在CentOS 8上卸载与安装MySQL 8的详细步骤

关键词:MySQL 8安装、CentOS 8、YUM源配置、卸载MySQL、MySQL残留文件删除、首次登录MySQL临时密码、服务状态检查、MySQL社区服务器 阅读建议:本文适合需要在CentOS 8操作系统上部署最新MySQL 8数据库的系统管理员或开发者阅读。文中步骤简洁清晰&#…

ResizeObserver loop completed with undelivered notifications.

报错信息 ResizeObserver loop completed with undelivered notifications. 来源 在用vue3 element-plus写项目的时候报的错,经过排查法,发现是element-plus的el-table组件引起的错误。 经过初步排查,这个错误并不是vue以及element-plus…

springboot投票统计管理系统的设计与实现-计算机毕业设计源码73598

摘 要 随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设投票统计管理系统。 …

最新扣子(Coze)实战教程:扣子​使用基础,完全免费,快来学习吧~

🧙‍♂️ 诸位好,吾乃斜杠君,编程界之翘楚,代码之大师。算法如流水,逻辑如棋局。 📜 吾之笔记,内含诸般技术之秘诀。吾欲以此笔记,传授编程之道,助汝解技术难题。 &#…

电脑由于ntdll.dlI丢失导致exe崩溃有什么解决办法?解决ntdll.dll丢失问题

相信有一些用户正在面临一个叫做“ntdll.dll丢失”的问题,这种情况多半发生在试图运行某个程序时,系统会提示一条错误消息:“程序无法启动,因为计算机中丢失了ntdll.dll”。那么,为何ntdll.dll文件会丢失,又…

OrangePi Kunpeng Pro 开箱测评之一步到喂

前情提要:大家好,我是Samle。有幸接到 CSDN 发来的测评邀请,下面针对 OrangePi Kunpeng Pro 开发板进行一些实践操作,让大家能更好的上手这块板子。 以下内容来自 官方说明 OrangePi Kunpeng Pro采用4核64位处理器AI处理器&#…