数据结构——链式二叉树的实现(详解)

呀哈喽。我是结衣。
不知道大家的递归学到怎么样呢?如果大家的递归功底不是很好,那么我相信在学完这篇文章后大家一定会对递归有一个更深层次的了解的。

构造链式二叉树

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

结构体的创建

typedef int BTdatatype;
typedef struct BinaryTreeNode
{
	BTdatatype data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}Treenode;

相信学了那么多数据结构的大家一定对此不会陌生,这里的结构体的创建的链表是相似的,毕竟这里的二叉树也是链式结构的。

函数的声明

Treenode* BTTreeCreat(BTdatatype x);
//前序
void PreOrder(Treenode* root);
//中序
void MidOrder(Treenode* root);
//求树的节点个数
int treesize(Treenode* root);
//求叶节点的个数
int TreeLeafSize(Treenode* root);
//求树的高度
int Treehight(Treenode* root);
//第k层的节点个数
int TreeLevelK(Treenode* root, int k);
//销毁
void treeDestroy(Treenode* root);
//查找
Treenode* BinaryTreeFind(Treenode*root,BTdatatype x);

没错学习二叉树就是为了让我们能够实现上面的功能。

构建二叉树

就想前文讲到的那样,现在我先面向结果构造一个如下图所示的二叉树出来,降低学习的成本,然后运用这棵树来实现二叉树的功能。
在这里插入图片描述

创造节点

Treenode* BTTreeCreat(BTdatatype x)
{
	Treenode* newnode = (Treenode*)malloc(sizeof(Treenode));
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

这个节点的创造和链表一模一样。不懂的小伙伴可以去看我实现链表的那篇文章。
节点创建完了,我们直接面向结果构造一个二叉树(如上图)

Treenode* root1 = BTTreeCreat(1);
	Treenode* root2 = BTTreeCreat(2);
	Treenode* root3 = BTTreeCreat(3);
	Treenode* root4 = BTTreeCreat(4);
	Treenode* root5 = BTTreeCreat(5);
	Treenode* root6 = BTTreeCreat(6);
	//Treenode* root7 = BTTreeCreat(7);
	root1->left = root2;
	root1->right = root4;
	root2->left = root3;
	root4->left = root5;
	root4->right = root6;
	//root5->right = root7;

构造完这个二叉树后我们就要来逐一的实现他的功能了。

函数的实现

我们要实现的功能有:打印二叉树的前序、中序,求二叉树的节点个数,求二叉树的叶节点个数,求二叉树的高度,求第k层的节点个数。

二叉树前序遍历

在实现这功能之前,我们先来了解一下上面是前序、中序、后序:

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
    简洁一点就是
    前序的访问顺序为根 左 右
    中序的访问顺序为左 根 右
    后序的访问顺序为左 右 根
    了解完毕,看代码
void PreOrder(Treenode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		printf("%d ", root->data);
		PreOrder(root->left);
		PreOrder(root->right);
	}
}

发现是递归了吗?没错要实现链式二叉树的遍历就是要用递归的。我们来分析一下这个代码:
递归的结束条件 节点为空时就返回
在这里插入图片描述
当我们进入函数1时,肯定是直接进入else中然后打印1,往下走,将root的左子树传入同函数,第二次进函数2打印2,再将2的左子树传进函数,再进入函数3打印3。这里要注意了,3的左子树为空,所以我们进入函数4的if里面打印N,然后再返回函数3,返回函数3后由于函数3还没执行完就继续往下执行,将3的右子树传给函数3-1,为空打印N,然后返回函数3此时函数3执行完毕退出函数3到函数2。后面差不多就略了,我们来画图看看。
在这里插入图片描述
就是这么调用的。
下面看看打印效果
在这里插入图片描述
和我们分析的一样。

中序遍历

中序遍历只需要把打印放再中间就可以了。

void MidOrder(Treenode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		MidOrder(root->left);
		printf("%d ", root->data);
		MidOrder(root->right);
	}
}

在这里插入图片描述
这就是它的打印结果大家可以自己分析分析。

求树的节点个数

求节点的个数,我们肯定就要遍历二叉树,所以肯定要用到递归(下面的函数实现也都要用到递归)
首先我们要确定递归的返回条件,当节点为空时我们就返回,并且我要返回0.

int treesize(Treenode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return treesize(root->left) + treesize(root->right) + 1;
}

这个代码的本质其实就是,如果该节点不为空就加1,为空就加0。因为在我们调用的每个函数里面只要不是空节点,那么它就会加1然后返回,假设我们已经进入3节点了,当我们继续执行函数的时候,它将3的左右子树再次进入函数,因为左右子树为0。返回0回到3节点再加1回到节点2然后同上。最后我们就能遍历二叉树求的节点的个数了。

在这里插入图片描述

求叶节点的个数

叶节点就是指:没有左右子树的节点。
实现前我们肯定要确定递归停止的条件。当节点为空时返回0。那么我们应该知道当当叶节点进入函数的时候叶要返回,这次返回1。然后就是递归调用。

int TreeLeafSize(Treenode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (!root->left && !root->right)
		return 1;
	else
		return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

在这里插入图片描述
打印结果也是没有问题的。

求树的高度

要想求树的高度,我们可以这要求:

int Treehight(Treenode* root)
{
	if (root == NULL)
		return 0;
	int lefthight = Treehight(root->left) + 1;
	int righthight = Treehight(root->right) + 1;
	return lefthight > righthight ? lefthight : righthight;

	/*int lefthight = Treehight(root->left);
	int righthight = Treehight(root->right);
	return lefthight > righthight ? lefthight+1: righthight+1;*/

	//return fmax()
}

求树的高度本质上就是找最长的一条线路,我们利用lefthight来保存左边最长的线路,用righthight来保存右边最长的线路,最后利用三目操作符的性质来返回最长的一条线路。
在这里插入图片描述
我们还可以再加一个节点7放在5节点的右边再验证一次。
在这里插入图片描述
事实上没有问题。

求第k层的节点个数

这个可以说是我们目前实现函数里面最难想到的一种了。首先我们要想怎么把他拆分成一个个的子问题。再把那个图取过来看看。
在这里插入图片描述
假设我们要求的是第3层节点的个数,是不是可以转换成2,3节点的第二层。3,5,6节点的第一层。有了这个思路来实现起来就会变得容易了。

int TreeLevelK(Treenode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	else
		return TreeLevelK(root->left, k - 1) + TreeLevelK(root->right, k - 1);

}

我们通过将k不断地减小,将问题的难度逐渐的压缩。最终得到一个不能再压缩的子问题,这就是递归的本质!
看看实现的效果。
我们来求第3层的节点个数(前文我加了一个节点进去,所以二叉树一共是有7个节点的)
在这里插入图片描述

在这里插入图片描述
没有问题,下面我就把我的原码放再下面了

代码

tree.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
typedef int BTdatatype;
typedef struct BinaryTreeNode
{
	BTdatatype data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}Treenode;

Treenode* BTTreeCreat(BTdatatype x);
//前序
void PreOrder(Treenode* root);
//中序
void MidOrder(Treenode* root);
//求树的节点个数
int treesize(Treenode* root);
//求叶节点的个数
int TreeLeafSize(Treenode* root);
//求树的高度
int Treehight(Treenode* root);
//第k层的节点个数
int TreeLevelK(Treenode* root, int k);
//销毁
void treeDestroy(Treenode* root);

tree.c

#define _CRT_SECURE_NO_WARNINGS
#include "tree.h"

Treenode* BTTreeCreat(BTdatatype x)
{
	Treenode* newnode = (Treenode*)malloc(sizeof(Treenode));
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
void treeDestroy(Treenode* root)
{
	assert(root);
	
}
void PreOrder(Treenode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		printf("%d ", root->data);
		PreOrder(root->left);
		PreOrder(root->right);
	}
}
void MidOrder(Treenode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	else
	{
		MidOrder(root->left);
		printf("%d ", root->data);
		MidOrder(root->right);
	}
}

int treesize(Treenode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return treesize(root->left) + treesize(root->right) + 1;
}

int TreeLeafSize(Treenode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (!root->left && !root->right)
		return 1;
	else
		return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
int Treehight(Treenode* root)
{
	if (root == NULL)
		return 0;
	int lefthight = Treehight(root->left) + 1;
	int righthight = Treehight(root->right) + 1;
	return lefthight > righthight ? lefthight : righthight;

	/*int lefthight = Treehight(root->left);
	int righthight = Treehight(root->right);
	return lefthight > righthight ? lefthight+1: righthight+1;*/

	//return fmax()
}

int TreeLevelK(Treenode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	else
		return TreeLevelK(root->left, k - 1) + TreeLevelK(root->right, k - 1);

}

test.c

#include "tree.h"



int main()
{
	Treenode* root1 = BTTreeCreat(1);
	Treenode* root2 = BTTreeCreat(2);
	Treenode* root3 = BTTreeCreat(3);
	Treenode* root4 = BTTreeCreat(4);
	Treenode* root5 = BTTreeCreat(5);
	Treenode* root6 = BTTreeCreat(6);
	Treenode* root7 = BTTreeCreat(7);
	root1->left = root2;
	root1->right = root4;
	root2->left = root3;
	root4->left = root5;
	root4->right = root6;
	root5->right = root7;
	PreOrder(root1);
	printf("\n");
	MidOrder(root1);
	printf("\n");
	printf("treesize:%d", treesize(root1));
	printf("\n");
	printf("treeleafsize:%d\n", TreeLeafSize(root1));
	printf("treehight:%d\n", Treehight(root1));
	printf("treelevelk:%d\n", TreeLevelK(root1, 3));
	
	return 0;
}


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为ospf和isis双点双向路由重分布的次优路径和环路终极解决方案

r5上直接导入直连路由 r3和r2进行双点双向路由重分布 查看R3去往R5产生了次优路径&#xff1a; 因为是R2先互相引入的isis和ospf&#xff0c;所以R3会产生次优路径&#xff0c;如果是R3先相互引入ospf和isis&#xff0c;那就是R2去R5会产生次优路径&#xff0c;而R3本身不会。…

[机缘参悟-120] :计算机世界与佛家看世界惊人的相似

目录 前言&#xff1a; 一、计算机 - 有序性不过是人为设计出来的&#xff01;&#xff01;&#xff01; 1.1 破相1&#xff1a;计算机的物质基础不过是一堆电子元器件的机缘组合 1.2 破相2&#xff1a;计算机不过是各种电信号的有序运动&#xff08;有序是关键&#xff09…

HarmonyOS 传感器开发指南

HarmonyOS 系统传感器是应用访问底层硬件传感器的一种设备抽象概念。开发者根据传感器提供的Sensor接口&#xff0c;可以查询设备上的传感器&#xff0c;订阅传感器数据&#xff0c;并根据传感器数据定制相应的算法开发各类应用&#xff0c;比如指南针、运动健康、游戏等。 运作…

【好用的个人工具】在Docker环境下部署Simple mind map思维导图工具

【好用的个人工具】在Docker环境下部署Simple mind map思维导图工具 一、Simple mind map介绍1.1 Simple mind map简介1.2 Simple mind map特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker co…

WEB渗透—反序列化(六)

Web渗透—反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩哔_…

Java 发送邮件

Java 发送邮件 使用Java应用程序发送E-mail十分简单&#xff0c;但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) 。 你可以在 JavaMail (Version 1.2) 下载最新的版本。 你可以再 在JAF (Version 1.1.1)下载最新的版本。 下载并解压这些文…

GO 集成Prometheus

一、Prometheus介绍 Prometheus&#xff08;普罗米修斯&#xff09;是一套开源的监控&报警&时间序列数据库的组合&#xff0c;起始是由SoundCloud公司开发的。随着发展&#xff0c;越来越多公司和组织接受采用Prometheus&#xff0c;社会也十分活跃&#xff0c;他们便…

Edge浏览器的跨域设置

关闭安全策略 复制一个浏览器的快捷方式&#xff0c;修改它的目标信息 在目标路径后加上这段命令&#xff1a;" --disable-web-security --user-data-dirD:/edgeCros" 没有引号&#xff0c;注意空格&#xff0c;D:/edgeCros是自定义文件夹&#xff0c;用来存放数据 …

livox 半固体激光雷达 gazebo 仿真 | 更换环境与雷达型号

livox 半固体激光雷达 gazebo 仿真 | 更换环境与雷达型号 livox 半固体激光雷达 gazebo 仿真 | 更换环境与雷达型号livox 介绍更换环境更换livox激光雷达型号 livox 半固体激光雷达 gazebo 仿真 | 更换环境与雷达型号 livox 介绍 览沃科技有限公司&#xff08;Livox&#xff…

2021-07-31

单日3亿日志数据准实时存储和分析 –ClickHouse 在自如大前端研发中心的应用 第一章 架构设计 和 用户体系建设 文章目录 单日3亿日志数据准实时存储和分析前言一、pandas是什么&#xff1f;二、使用步骤1.引入库2.读入数据 总结 前言 用户行为数据的收集和分析&#xff0c;…

AI 学习笔记(持续更新)

What is AI PS &#xff1a;代码块里的统一是 gpt4 回复 在大模型中 1 b 10 亿参数的含义 AI 目前是什么&#xff1f; 目前的人工智能&#xff08;AI&#xff09;是指使计算机和机器能够模仿人类智能的技术&#xff0c;包括学习、推理、解决问题、知觉、语言理解等能力。A…

如何根据接口文档,轻松快速的模拟接口服务?

什么是WireMock? WireMock 是一个Http 模拟服务,其核心也是一个web服务,WireMock主要是为特定请求提供固定的返回值。 WireMock可以作为单独进程启动,模拟一个WEB服务器,提供一些API访问,并返回特定的返回值。也可以作为第三方库在项目中使用。 如何使用 standalone方…

HelpLook可以作为wordpress的替代品,帮助企业快速搭建博客

博客作为一个非常有价值的平台&#xff0c;在当今的数字时代具有重要的意义。对于个人和企业来说&#xff0c;选择一款适合自己需求的专业博客搭建软件至关重要。本篇文章将会通过对比两个专业的博客搭建软件——HelpLook和WordPress&#xff0c;看看为什么我说HelpLook可以作为…

华为P40无法链接adb的解决记录

真的很讨厌华为的设备&#xff0c;很多东西啥设备都能跑得好好的&#xff0c;就华为会出问题&#xff0c;简直就是手机界的IE。 情况&#xff1a;突然无法链接adb到P40&#xff0c;拔插无效&#xff0c;关闭开发人员选项再打开也无效&#xff0c;撤销USB调试授权也无效&#x…

英伟达“阉割版”AI芯片遇阻,推迟至明年发布 | 百能云芯

近日&#xff0c;英伟达&#xff08;Nvidia&#xff09;为遵守美国出口规定而推迟在中国市场推出的新款人工智能&#xff08;AI&#xff09;芯片引起了业界广泛关注。 据路透社报道&#xff0c;两位消息人士透露&#xff0c;该芯片被命名为H20&#xff0c;是英伟达为遵守美国最…

文本编辑 换行符CRLF/CR/LF问题

参考资料 Linux—CRLF/CR/LF等回车换行符问题详解改行コードCRはなぜ&#xff08;^M&#xff09;で\rなのかテキストファイルの行末に^Mが表示されるLinux 替换^M字符 方法 目录 一. 遇到的问题二. 换行符释义三. 换行符查看四. 去除 ^M4.1 通过文本编辑器转换换行符4.2 在lin…

【C++初阶(九)】 priority_queue的使用与模拟实现

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

多平台小程序编译适配,是否会让更多App互联互通?

随着科技的飞速发展&#xff0c;我们正迅速进入一个以数字化为主导的时代。 在这个时代中&#xff0c;通信、小程序、快应用、云服务器等平台连接类软件如火如荼的发展&#xff0c;手机、手表、AR/VR眼镜等智能移动穿戴设备迅速的升级迭代&#xff0c;5G、芯片、算力等基础设施…

代码随想录算法训练营 ---第四十三天

前言&#xff1a; 今天同样是01背包问题&#xff0c;今天详细学习了背包问题在各种场景下的应用。今天一道也没做出来&#xff0c;有点废。好难啊&#xff01;就是思路不太清晰&#xff0c;不知道如何去做&#xff0c;看了题解后感觉原来如此&#xff0c;但是想不出来。今天做…

软件提示找不到“vcruntime140.dll丢失的五个解决方法”(有效方法)

“vcruntime140.dll丢失的五个解决方法”。在我们的日常生活和工作中&#xff0c;有时候会遇到一些电脑问题&#xff0c;而vcruntime140.dll丢失就是其中之一。那么&#xff0c;什么是vcruntime140.dll文件呢&#xff1f;它为什么会丢失&#xff1f;又该如何解决这个问题呢&…
最新文章