数据结构之链式二叉树

news2025/1/8 5:48:06

当我们初步了解二叉树后

我们就可以进一步去深入学习二叉树了

1.链式二叉树的遍历

这里我们先去定义链式二叉树的结构

分为两个指针

一左一右

他们分别指向左子树和右子树

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinartTreeNode* left;
	struct BinartTreeNode* right;
}TreeNode;

左右子树的节点又可以细分为:根,左子树,右子树

图中的1节点就是根,2和3则是左子树,456则是右子树

1.1二叉树的前序,中序,后序遍历


前序遍历、中序遍历和后序遍历。这些遍历方式指的是节点访问的顺序。

前序遍历:在前序遍历中,我们首先访问根节点,然后递归地进行左子树的前序遍历,接着递归地进行右子树的前序遍历。


中序遍历:中序遍历中,我们首先递归地进行左子树的中序遍历,然后访问根节点,最后递归地进行右子树的中序遍历。


后序遍历:后序遍历中,我们首先递归地进行左子树的后序遍历,然后递归地进行右子树的后序遍历,最后访问根节点。

而这里呢我们又遇到了老朋友递归

问题不大

我们利用一棵树为例子

图中的这一棵树

以一为根

接下来我们先进行前序遍历

1.1.1前序遍历

前序遍历中先根后左右

●所以我们先去访问根1

●访问完根1后就访问左节点2

●接下来以2为根访问2的左节点3

●然后以3为根访问3的左节点,这是他的左节点为空,所以我们返回也就是开始进行递归

●递归到一后,开始进行访问1的右节点4

●访问到四就以4为根访问4的左节点5

●访问5后发现没有左节点就递归到4访问4的右节点

●访问6时没有左节点右节点就开始递归到4再到1

●最后访问结束

这里呢,我们用N代表空

那么访问完打印后应该是这样的

1 2 3 N N N 4 5 N N 6 N N

1.1.2中序遍历

讨论完前序遍历我们进行中序遍历

中序遍历讲的是先左后根最后右

还是利用这棵树

遍历顺序:

●先是访问左子树,1的左子树是2,2的则是3,3没有了左子树也就是空,返回3,然后在访问3的右子树,空,返回到2

这是返回的应该是

N 3 N

●回到2后访问2的左子树空,所以返回到1

N 3 N 2 N

●回到一就访问1的右子树4,然后到4的左子树5,在访问5的左子树空,这时返回到5

N 3 N 2 N 1 N 5 N

这时会有疑问,为什么没有四,不是先访问到4吗?

这里没有4的原因是,返回到1后应该访问它的右子树,而右子树中还有一个左子树5,所以应该先访问5,这里的5优先访问

●然后返回到4,访问4的右子树6,这里优先访问6的左子树,为空,返回到6,访问右子树,为空

N 3 N 2 N 1 N 5 N 4 N 6 N

1.1.3后序遍历

后序遍历则是先左后右最后根

遍历顺序:

还是以这棵树为例


●先访问1的左子树,到3时,他的左子树为空,右子树为空

N N 3

●返回到2后,右子树为空,访问根2,返回1

N N 3 N 2

●回到一后,访问一的右子树,同时优先访问右子树中的左子树也就是节点五,访问五的左子树,然后是右子树,然后是五

 N N 3 N 2 N N 5

●J返回到五后访问4的右子树6,然后访问6的左子树和右子树

N N 3 N 2 N N 5 N N 6

●访问完6后就返回访问4,然后访问1

N N 3 N 2 N N 5 N N 6 4 1

2.遍历代码的实现

2.1.树的实现

首先我们需要手搓一棵树

定义出来树的结构

并需要创建新的空间,所以这里包装一个函数

typedef struct BinTreeNode
{
	struct BinTreeNode* left;
	struct BinTreeNode* right;
	int val;
}BTNode;
BTNode* BuyBTNode(int val)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->val = val;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
// 手搓一棵树
BTNode* CreateTree()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;

	return n1;
}

这样就形成了图形中的树

2.2前序遍历代码

前序遍历的话我们需要用到递归

首如果检测到节点为空,这样的话就打印N并返回

如果没有

那么递归继续往下

因为是前序遍历

所以先递归左然后再递归右

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

	printf("%d ", root->val);
	PreOrder(root->left);
	PreOrder(root->right);
}

2.3中序遍历代码

中序遍历和前序遍历的不同是前序遍历先根后左右,中序遍历则是先左后根最后右

所以,我们还是先遇到空返回N

没有则是返回左,打印根ra

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

	InOrder(root->left);
	printf("%d ", root->val);
	InOrder(root->right);
}

2.4后序遍历代码

后序遍历代码则是先左右最后根

所以

void PostOrder(BTNode* root)
{
	if (root = NULL)
	{
		printf("N");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

2.5测试的结果

3.获取节点个数

获取节点个数,正常情况下我们的思路是定义一个size

然后在遍历的时候进行++size

代码如下

int TreeSize(BTNode* root)
{
	static int size = 0;
	if (root == NULL)
		return 0;
	else
		++size;

	TreeSize(root->left);
	TreeSize(root->right);
	return size;
}

但这样会有一个缺点

我们没法去在这个函数里面重置我们的size

所以我们需要再主函数中

每调用完TreeSize函数,就需要重置一遍size

所以我们还有另外一个思路

直接去返回它的左节点和右节点,最后加一

利用递归的思想

代码如下

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

这样就非常巧妙的完成了节点的个数

1.获取叶节点个数
获取叶子结点个数,我们这里也用递归的方法

利用分治思想去解决这个问题

●代码思想:

1. 当遇到空树或者遇到空的节点时,也就是说这是的叶子为NULL,这是我们返回0

2. 当遇到左节点或者右节点为空,当节点不为空时,此时已经到达了叶子结点,所以返回1

3. 当遇到的不是叶节点时,我们需要到递归左节点的个数和右节点的个数,并进行递归返回

●代码思想:

对于整棵树来说,当我们遇到空树或者遇到节点为空的时候,这时的叶子结点为空,我们这时返回0,当不是上中情况的时候,我们从根往下去搜索,先搜索左节点,当左节点不为空,并且左节点的左子树和右子树都是空的时候,这时候就可以确定它是叶子了,也就是返回1,当搜索完左子树就可以搜索右子树,右子树也同理 

4.获取树的高度 

获取树的高度,我们也是利用分治的思想去实现这个代码

首先就是当我们要想返回高度的时候,我们需要调用到左右子树的高度

然后比较左右子树的高度,比较出最大的一个并返回

然后加1(因为我们递归的是左右子树的高度,我们需要整个树的高度,所以还需要加上根,也就是加一)

●代码思想:

1.当我们遇到空树或者遇到的节点为NULL,这时返回0

2.然后接下来去递归左子树和右子树

3.返回时,如果左子树大于右子树,那么就是左子树高度+1,否则右子树高度+1

 

//获取树的高度
int TreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	TreeHeight(root->left);
	TreeHeight(root->right);

	return (TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left) : TreeHeight(root->right)) + 1;
}

但这个代码有一定的缺陷

我们可以看到,这个代码我们调用了两次TreeHeight(root->left)和TreeHeight(root->right)

在这一树中,我们调用多次函数,大大增加了计算的难度,在一棵小树中可能不明显,可当树更大时,这时候弊端就先显示出来了

所以我们可以改进一下代码,定义两个变量去接受返回值

然后比较两个返回值

//改进代码
int TreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	/*TreeHeight(root->left);
	TreeHeight(root->right);

	return (TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left) : TreeHeight(root->right)) + 1;*/
	int Heightleft = TreeHeight(root->left);
	int Heightright = TreeHeight(root->right);

	return (Heightleft > Heightright ? Heightleft : Heightright + 1);
}

 

5.计算第K层节点个数 

计算k层节点的个数,我们可以看成计算左节点的(k-1)层和右节点(k-1)层的节点个数

因为我们不算顶部节点所以应该是k-1

●代码思想:

首先是如果是空树或者当遇到叶子结点外的空节点时,返回0

当遇到k为1的时候,这时只有一个根,也就返回1

其余情况均利用递归思想,去递归左右子树,注意此时的k应该变成k-1

//计算树k层的节点个数
int TreeKCount(BTNode* root, int k)
{
	if (root == NULL || k < 1)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeKCount(root->left, k - 1) + TreeKCount(root->right, k - 1);
}

 

6.寻找某个节点 

寻找某个节点的话,我们也利用递归的方法,分治的思想去解决这个问题

寻找某个节点,那么这个节点如果不在根上,那么就在根的左子树和右子树上

那么就想下寻找

下边的节点也可以分为左子树和右子树和根

依次进行,就形成了递归

//寻找某个节点
BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)
	{
		return 0;
	}
	if (x == root->val)
	{
		return root;
	}
	TreeFind(root->left, x);
	TreeFind(root->right, x);
    return NULL;
}

很多人可能会想到这样的代码

可当我们去运行的时候,我们会发现找不到,不管x为多少都找不到

为什么呢?

原因是我们没有东西去接收

当我们找到的时候,我们递归需要往上递归

可上边的栈中没有可以接受的变量值

所以我们最终遍历完整棵树也找不到我们想找的节点

所以改一下代码

//寻找某个节点
BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)
	{
		return 0;
	}
	if (x == root->val)
	{
		return root;
	}
	BTNode* ret1 = TreeFind(root->left, x);
	BTNode* ret2 = TreeFind(root->right, x);
	if (ret1)
		return ret1;
	if (ret2)
		return ret2;
	return NULL;
}

 

这样我们利用新建立的节点去接受我们的左右子树的数据

然后如果不为空就不断返回,为空那么就返回0

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

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

相关文章

气液分离器的选型介绍

气液分离器在热泵或制冷系统中的基本作用是分离出并保存回气管里的液体以防止压缩机液击。因此,它可以暂时储存多余的制冷剂液体,并且也防止了多余制冷剂流到压缩机曲轴箱造成油的稀释。因为在分离过程中,冷冻油也会被分离出来并积存在底部,所以在气液分离器出口管和底部会…

Mybatis的简介和实现增删改查

第一章&#xff1a;框架的概述 JDBC存在的问题 我们要想研究mybatis就必须知道jdbc所存在的问题&#xff0c;那我那么我们首先来复习一下jdbc操作数据库的大致流程。 1.加载数据库驱动 2.创建并获取数据库连接对象connection 3.通过连接对象获取会话对象statement 4.编写sql…

Docker 系列2【docker安装mysql】【开启远程连接】

文章目录 前言开始步骤1.增加mysql挂载目录2.下载镜像2.启动容器具体步骤4.无法连接5.测试连接 总结 前言 本文开始&#xff0c;默认已经安装docker&#xff0c;如果你还没有完成这个步骤&#xff0c;请查看这一篇文章【docker安装与使用】 开始步骤 1.增加mysql挂载目录 m…

考研模拟面试-答案【攻略】

考研模拟面试-答案【攻略】 前言版权推荐考研模拟面试-答案前面的问题通用问题专业题数据结构计算机网络操作系统数据库网络安全 手写题数据结构操作系统计算机网络 代码题基础代码题其他代码题 后面的问题补充题目 基础代码题答案链栈循环队列1循环队列2哈希表 最后 前言 202…

pytorch升级打怪(三)

数据集合数据加载器 简介加载数据集迭代和可视化数据集为您的文件创建自定义数据集__init____len____getitem__ 准备您的数据以使用DataLoaders进行训练通过DataLoader进行遍载 简介 处理数据样本的代码可能会变得混乱且难以维护&#xff1b;理想情况下&#xff0c;我们希望我…

C++笔记:从零开始一步步手撕高阶数据结构AVL树

文章目录 高度平衡二叉搜索树实现一颗AVL树结点与树的描述——定义类AVL树的插入操作步骤1&#xff1a;按照二叉搜索树的方法插入结点步骤2&#xff1a;自底向上调整平衡因子步骤3&#xff1a;触发旋转操作&#xff08;AVL树平衡的精髓&#xff09;右单旋左单旋左右双旋右左双旋…

AtomoVideo:AIGC赋能下的电商视频动效生成

✍&#x1f3fb; 本文作者&#xff1a;凌潼、依竹、桅桔、逾溪 1. 概述 当今电商领域&#xff0c;内容营销的形式正日趋多样化&#xff0c;视频内容以其生动鲜明的视觉体验和迅捷高效的信息传播能力&#xff0c;为商家创造了新的机遇。消费者对视频内容的偏好驱动了视频创意供给…

C语言基础之结构体

文章目录 一、结构体1、结构体概述2、结构体类型的定义方式&#xff08;1&#xff09;先定义结构体类型&#xff0c;再定义结构体变量&#xff08;2&#xff09;结构体类型、变量同时定义&#xff08;3&#xff09;一次性结构体 3、结构体成员的初始化(1)结构体初始化(2)清空结…

linux用git拉取我云端以及git处理冲突

拉取后切换一个跟云端分支(dev)一样的 git branch --set-upstream-toorigin/dev dev 之后就同步了 A在dev分支写了iii,提交 B在dev分支写了hhh,提交,冲突 怎么修改,B把云端的拉下来,随便改改就行

找准方向选CRM客户管理系统!2023年排行榜推荐

本文将为大家带来2023有哪些好用CRM客户管理系统&#xff1f;CRM系统排行榜基于品牌知名度、功能、产品实力、系统稳定性、用户体量等多重因素考量。其中Zoho CRM、红圈CRM等产品市场表现优异入选此次榜单。 1.Zoho CRM 公司成立时间&#xff1a;1996年 Zoho&#xff08;卓豪…

你为什么是你,而不是别人?认识人格的力量

你为什么是你&#xff0c;而不是别人&#xff1f;让你做自我介绍&#xff0c;你会怎么描述自己呢&#xff1f; 人格心理学是心理学的一门重要分支学科。探求、描述和揭示个体思想、情绪及行为的独特模式&#xff0c;综合个人与环境诸多影响因素&#xff0c;对现实社会中的个人作…

jenkins部署go应用 基于docker-compose

丢弃旧的的构建 github 拉取代码 指定go的编译版本 安装插件 拉取代码是排除指定的配置文件 比如 conf/config.yaml 文件 填写配置文件内容 比如测试环境一些主机信息 等 可以配置里面 构建的时候选择此文件替换开发提交的配置文件。。。。 编写docker-compose 文件 docker…

【PythonCode】力扣Leetcode6~10题Python版

【PythonCode】力扣Leetcode6~10题Python版 前言 力扣Leetcode是一个集学习、刷题、竞赛等功能于一体的编程学习平台&#xff0c;很多计算机相关专业的学生、编程自学者、IT从业者在上面学习和刷题。 在Leetcode上刷题&#xff0c;可以选择各种主流的编程语言&#xff0c;如C、…

渐开线花键不是齿轮?

在和一位小伙伴交流时&#xff0c;他认为齿轮和花键不一样&#xff0c;那花键是不是齿轮呢&#xff1f;老师傅们可以绕开了&#xff0c;我觉得对于一些平时接刚刚接触齿轮&#xff0c;或者很少接触的朋友来说&#xff0c;还是有必要聊一聊这个话题。 首先这个问题并不严谨&…

包冲突解决之-invalid constant type: 18

背景 现象一&#xff1a;引入了一个包A&#xff0c;服务突然起不来了&#xff0c;后台有报错信息&#xff0c;Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type xxx available: expected at least 1 bean which quali…

CSS扩展选择器

文章目录 1. 并集选择器2. 交集选择器3. 后代选择器4. 子代选择器5. 兄弟选择器5.1. 相邻兄弟选择器5.2. 通用兄弟选择器 6. 属性选择器7. 伪类选择器7.1. 动态伪类7.2. 结构伪类7.3. 否定伪类 8. 伪元素选择器9. Google 改进案例 1. 并集选择器 选中多个选择器对应的元素。一…

Day44-sersync企业实时复制实战

Day44-sersync企业实时复制实战 1. sersync实时复制工具介绍1.1 sersync工具简介1.2 sersync特点1.3 sersync图解原理1.4 sersyncrsync实时复制方案项目实践1.4.1 图解项目方案架构及实现原理1.4.2 确保远程数据传输服务部署完成1.4.3 检查当前系统nfs01是否支持inotify实时监控…

智慧城市:提升城市治理能力的关键

目录 一、智慧城市的概念及特点 二、智慧城市在提升城市治理能力中的应用实践 1、智慧交通&#xff1a;提高交通治理效率 2、智慧政务&#xff1a;提升政府服务水平 3、智慧环保&#xff1a;加强环境监测与治理 4、智慧安防&#xff1a;提高城市安全水平 三、智慧城市在…

基于springboot的七彩云南文化旅游网站的设计与实现(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装七彩云南文化旅游网站软件来发挥其高效地信息处理的作用&am…

sqllab第二十三关通关笔记

知识点&#xff1a; mysqli_query() 返回值为资源型或布尔型如果内容为查询语句则返回资源型数据&#xff1b;如果内容为插入、更新、删除等语句则返回布尔类型结果mysql_fetch_array() 从结果集中取出一行作为关联数组或数字数组输入内容为指定查询的结果集单引号闭合绕过联…