数据结构与算法—“二叉树”的实现

news2024/11/25 2:47:49

目录

 一、二叉树链式结构的实现

 1、声明结构体

2、创建新节点

3、创建二叉树 

二、二叉树的遍历

1、前序遍历讲解

 2、节点个数

3、叶子节点个数

4、二叉树的高度

5、第k层节点个数

6、查找值为x的节点

完整版代码: 


 一、二叉树链式结构的实现

 1、声明结构体

我们为二叉树的节点创建BTNode结构体,成员包含数据data、左节点和右节点的指针。 

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

2、创建新节点

为新节点分配空间并初始化 。

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

3、创建二叉树 

我们创建七个节点,将它们连接起来,使其成为下图的形态:

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->left = node7;

	return node1;
}

二、二叉树的遍历

二叉树的遍历是一种系统地访问其所有节点的方法,这对于深入理解树的结构和特性至关重要

遍历,本质上,是按照某一确定的次序,逐一访问二叉树中的每个节点,确保每个节点都被访问一次,并且只被访问一次。这种访问操作可以是任何针对节点的处理,具体取决于应用场景。

基于节点访问的先后次序,二叉树的遍历可以分为前序、中序和后序三种主要类型:

  • 前序遍历(亦称为先序遍历):首先访问根节点,然后遍历左子树,最后遍历右子树。
  • 中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树。
  • 后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点。

这三种遍历方法都采用递归的方式来访问节点,为我们提供了一个明确且系统的方式来处理和理解二叉树。

我们用刚刚创建的二叉树来写出前、中、后序:

接下来,我们用代码实现遍历 ,验证咱们书写的前、中、后序是否正确:

1、前序遍历讲解

我们以前序遍历的方法为例进行讲解,中序和后序的遍历方法与其大同小异

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

	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
  • 接收参数为根节点。
  • 如果当前节点为空则输出打印N(表示NULL),不为空则输出当前节点的值。
  • 递归调用PrevOrder函数遍历当前节点的左子树。
  • 递归调用PrevOrder函数遍历当前节点的右子树。

函数会将当前节点的左子节点和右子节点作为参数传递给自身。这样,PrevOrder函数就可以在遍历完当前节点后,继续遍历左右子树。当遍历到叶子节点时,PrevOrder函数会直接返回,结束递归调用。

递归全过程如下图:(红色数字表示递归顺序)

 

前、中、后序遍历完整代码: 

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

int main()
{
	BTNode* root = CreatBinaryTree();
	PrevOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");
    return 0;
}

根据输出结果可知我们之前的计算结果正确!!!

 2、节点个数

以下两种方式都可以,第二种比较简洁,二者效果相同。 

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

	return BTreeSize(root->left)
		+ BTreeSize(root->right)+ 1;
}
int BTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BTreeSize(root->left)
		       + BTreeSize(root->right) + 1;
}
  • 在函数内部,首先判断当前节点是否为空,
  • 如果为空则返回0。
  • 如果当前节点不为空,则递归调用BTreeSize函数计算当前节点的左子树和右子树中节点的个数,然后将它们相加,并加上当前节点本身,即可得到整个二叉树中节点的个数。

3、叶子节点个数

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。
  • 如果当前节点不是叶子节点,则递归调用BTreeLeafSize函数计算当前节点的左子树和右子树中叶子节点的个数,然后将它们相加,即可得到整个二叉树中叶子节点的个数。

4、二叉树的高度

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;
}
  • 在函数中,首先判断当前节点是否为空。

  • 如果当前节点为空,返回0。

  • 如果当前节点不为空,则递归计算其左右子树的高度,分别保存在leftHeight和rightHeight变量中。

  • 最后,通过比较左右子树的高度,得到较大值并加1,即为整个二叉树的高度。
  • 这里使用了三目运算符,如果leftHeight大于rightHeight,则返回leftHeight+1,否则返回rightHeight+1。

5、第k层节点个数

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);
}
  • 在函数内部,首先判断当前节点是否为空,如果为空则返回0。
  • 如果当前节点不为空,则判断当前层数是否为1。
  • 如果当前层数为1,则返回1。
  • (图中假设求第三层节点个数,左侧红色数字为函数内k随层数的变化
  • 如果当前层数不为1,则递归调用BTreeLevelKSize函数计算当前节点的左子树和右子树中第k-1层节点的个数,然后将它们相加,即可得到整个二叉树中第k层节点的个数。

6、查找值为x的节点

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。

  • 如果当前节点的值等于目标值x,说明已经找到了目标节点,返回该节点指针。

  • 如果当前节点不是目标节点,则递归查找其左右子树。

  • 首先在左子树中查找目标值,如果找到则返回该节点指针;

  • 如果在左子树中未找到,则在右子树中查找目标值,如果找到则返回该节点指针。

  • 如果左右子树中都未找到目标值,则返回NULL。 

查找值为 4 的节点过程如下: 

 

完整版代码: 

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

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;
	}
	node->data = x;
	node->left = node->right = NULL;
	return node;
}

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	//BTNode* node7 = BuyNode(7);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	//node5->left = node7;

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

int BTreeSize(BTNode* root)
{
    //两种都可以
	/*if (root == NULL)
		return 0;

	return BTreeSize(root->left)
		+ BTreeSize(root->right)
		+ 1;*/

	return root == NULL ? 0 : BTreeSize(root->left)
		+ BTreeSize(root->right) + 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);
}

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


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

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

int main()
{
	BTNode* root = CreatBinaryTree();
	PrevOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

	//printf("BTreeSize:%d\n", BTreeSize(root));

	//printf("BTreeLeafSize:%d\n", BTreeLeafSize(root));

	//printf("BTreeLeafSize:%d\n", BTreeLeafSize(root));

	//printf("BTreeLevelKSize:%d\n", BTreeLevelKSize(root, 3));
	//printf("BTreeLevelKSize:%d\n", BTreeLevelKSize(root, 4));

	return 0;
}

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

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

相关文章

【每日一练 | 华为认证真题练习Day11】

华为真题从Day1 开始的真题可留言获取 ​ Day11 华为认证中级考试真题 1、下面哪些是对网络进行管理的主要目标?(多选) A.确保网络用户收到期望的网络服务质量与技术服务信息 B.减少设备的搬迁费用 C.减少网络设备使用年限&#xff0c;和延长服务周期 D.帮助网络工程师…

nebula-br local-store 模式,快速搭建主备集群实践

因为线上图数据库目前为单集群&#xff0c;数据量比较大&#xff0c;有以下缺点&#xff1a; 单点风险&#xff0c;一旦集群崩溃或者因为某些查询拖垮整个集群&#xff0c;就会导致所有图操作受影响很多优化类但会影响读写的操作不好执行&#xff0c;比如&#xff1a;compact、…

HANA:计算视图-图形化视图-“参数“-引用表数据(性能优化)

1.前言 最近项目HANA部分的开发用的比较多&#xff0c;之前很少用图形化计算视图&#xff0c;最近研究了下&#xff0c;发现有些小功能对于图形化视图的性能提升&#xff0c;还有建模便利性都有很大帮助&#xff0c;今天发现了一个小功能&#xff0c;就是视图中的参数&#xf…

两种办法实现进制转换:将十进制数N转换成为r进制数

两种办法实现进制转换&#xff1a;将十进制数N转换成为r进制数 法一&#xff1a;递归 //进制转换 void change1(int N,int r) {if (N / r 0) {printf("%d", N%r);}else {change1(N/r, r);printf("%d", N%r);} }int main() {int N 0;int r 0;printf(&q…

grid布局中grid-row和grid-column

目录 一、grid-row 二、grid-column 三、实例 一、grid-row grid-row属性定义了网格元素行的开始和结束位置。结合了grid-row-start和grid-row-end grid-row: 1 / 3;//表示行线从第一行线到第三行线为止 二、grid-column grid-column属性定义了网格元素列的开始和结束位置…

企业税收违法查询API:提升财务监控和风险管理的关键利器

引言 随着企业税务环境的不断演变和政府对税收合规性的日益关注&#xff0c;企业面临着更多的税务合规挑战。为了降低税务风险&#xff0c;提高财务监控水平&#xff0c;许多企业已经开始采用先进的工具和技术。其中之一&#xff0c;便是企业税收违法查询API&#xff0c;这一强…

大神总结:做数据可视化,谨记三要、两不要

直观灵活分析数据&#xff0c;让数据更易懂的数据可视化报表是由大量不同类型的数据可视化图表组成的&#xff0c;那数据可视化图表又是怎么制作出来的&#xff1f;在制作数据可视化报表的过程中需要注意哪些事项&#xff1f; BI数据可视化工具里预设大量的数据可视图表&#…

魔术般的速度,焕然一新的磁盘空间 - Magic Disk Cleaner for Mac 2023

在当今这个信息时代&#xff0c;我们的磁盘空间无时无刻不在被各种文件和数据所填满。无论是工作文件&#xff0c;还是日常生活的照片、视频&#xff0c;亦或是下载的各种应用程序&#xff0c;都在不断地蚕食着我们的磁盘空间。面对这种情况&#xff0c;一款高效、便捷的磁盘垃…

竞赛选题 深度学习实现行人重识别 - python opencv yolo Reid

文章目录 0 前言1 课题背景2 效果展示3 行人检测4 行人重识别5 其他工具6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的行人重识别算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…

用友U8+ CRM任意文件上传漏洞

用友U8 CRM任意文件上传漏洞 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: body"用友U8CRM" 漏洞复现1. 构造poc2. 复现3. 访问webshell 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不得利用该…

I/O性能优化——这一篇就足够啦

背景 继上一篇CPU性能优化文章 &#xff0c;本次向大家分享关于I/O性能优化的分析套路以及常见措施。后续还有关于内存及网络优化的篇章。 基本概念 对于I/O我们先了解几个概念&#xff0c;文件系统&#xff0c;磁盘&#xff0c;文件。 磁盘 磁盘为系统提供了最基本的持久化存…

香港高端人才通行证计划入围高校/全球百强大学综合名单公布!

香港高端人才通行证计划入围高校/全球百强大学综合名单公布&#xff01; 香港高才通计划希望吸引世界各地具备丰富工作经验及高学历的人才到香港探索机遇&#xff0c;这些高端人才包括高收入人士和在世界顶尖大学毕业的学生。 此计划并不适用于阿富汗、古巴、老挝、朝鲜、尼泊尔…

低功耗WiFi模块的技术发展

随着物联网的迅速发展&#xff0c;对于低功耗设备和技术的需求日益增加。低功耗WiFi模块应运而生&#xff0c;为连接大量设备提供了更长的电池寿命和更可持续的能源解决方案。本文将深入研究低功耗WiFi模块的技术发展&#xff0c;探讨其在物联网和移动设备领域的关键作用。 1.…

LEEDCODE 2235两整数相加

class Solution { public:int sum(int num1, int num2) {return (num1 num2);} };

AD9361 数据数字接口说明

一、简要 AD9361和BBP之间的数据接口以两种模式之一工作&#xff1a;标准CMOS兼容模式或低压差分信号&#xff08;LVDS&#xff09;兼容模式。本篇文章将简要介绍一下CMOS和LVDS工作模式下的数据具体的传输样式。 二、CMOS 工作模式 P0_D[11:0] and P1_D[11:0]: 端口0&#xf…

Firefox修改缓存目录的方法

打开Firefox&#xff0c;在地址栏输入“about:config” 查找是否有 browser.cache.disk.parent_directory&#xff0c;如果没有就新建一个同名的字符串&#xff0c;然后修改值为你要存放Firefox浏览器缓存的目录地址&#xff08;E:\FirefoxCacheFiles&#xff09; 然后重新…

继承访问限定

C施雷老师课堂笔记

鸿蒙问题记录

1、Variables decorated by Prop link, "Consume, and Obiectlink cannot be initialized locally 原因&#xff1a;被装饰器修饰的数据&#xff0c;不能初始化。这个应该是后续版本做了优化。当前使用 DevEco Studio 3.1.1 Release

java入门,JSONObject实现源码解析

一、前言 现在写java程序&#xff0c;很少需要写一些底层的数据结构和算法&#xff0c;因为这些轮子早已造好&#xff0c;拿来用就行。比如在代码中我们经常用到的这个类JSONObject &#xff0c;还有我们经常使用的String类型&#xff0c;它都是有底层实现的&#xff0c;我们直…

IDEA如何显示左右侧的工具栏和选项栏固定位置折叠隐藏

项目场景 最近使用idea的时候&#xff0c;发现右边框和下边框的选项栏没掉了&#xff0c;直接影响到了开发效率。 原因分析&#xff1a; 应该是 键盘误触&#xff0c;触发隐藏边框的快捷键。 解决方案&#xff1a; 如下图操作&#xff0c;选项栏就出来了&#xff1a;