二叉树的实现(递归实现)

news2024/11/17 11:54:12

前言:本文讲解通过递归的方式实现二叉树的一些基本接口。

目录

通过左右子树的方式实现二叉树:

二叉树的遍历:

 求二叉树结点的个数:

二叉树所有节点的个数:

二叉树叶子节点的个数: 

求第k层节点的节点的个数 :

找二叉树中值为x的节点

求二叉树的高度:

构建二叉树:

销毁二叉树:

重头戏:中序遍历

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


通过左右子树的方式实现二叉树:

结构体的定义:

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

二叉树的遍历:

前序:根 ,左子树, 右子树

中序:左子树 ,根 ,右子树

后序: 左子树, 右子树, 根

根据上图分别走一遍前中后序:

前序:A ,B,NULL,NULL,C,NULL,NULL

中序:NULL,B,NULL,A,NULL,C,NULL

后序:NULL,NULL,B,NULL,NULL,C,A 

想要理解前中后序需要了解递归

每一棵树都可以看成 根   左子树   右子树三部分组成。

代码实现:

//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("null ");
		return;
	}

	printf("%c ", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

深入理解代码:

以下是上面代码的函数递归调用的代码走读情况。

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("null ");
		return;
	}


	BinaryTreeInOrder(root->left);
	printf("%d ", root->val);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("null ");
		return;
	}


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

 求二叉树结点的个数:

二叉树所有节点的个数:

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

将问题分解:

如果 root为NULL,返回0;

每个二叉树结点的个数等于左子树节点的个数+右子树节点的个数+根(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为NULL,返回0;

如果左右子树为NULL,该节点为叶子节点就返回1;

二叉树的叶子节点的个数等于左子树叶子节点的个数+右子树叶子节点的个数。

求第k层节点的节点的个数 :

代码:

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root,int k)
{
	if (root == NULL)
		return 0;
	if (k==1)//此时root不等于空
		return 1;

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

分解问题:

将问题可以看成: 第K层节点的个数 = 左子树第K-1层节点的个数+右子树K-1层节点的个数

如果root为NULL,返回0;

如果层数为1且该root不为NULL时,返回1


找二叉树中值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->val == 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;
}

注意:要创建一个变量接受返回值,然后再返回,如果直接用函数返回返回值,那么就会出现多次重复调用.

分解问题:

找整棵树的问题转化为,找左子树和找右子树的问题

如果root为NULL,返回NULL

如果找到root->val==x,直接返回该节点的地址,

如果左右子树都没有x这个值ULL.

求二叉树的高度:

代码:

//求二叉树的高度
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftheight = BinaryTreeHeight(root->left);
	int rightheight = BinaryTreeHeight(root->right);

	return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}

 问题分解:

二叉树的高度 = 左右子树中高度最高的高度+1(根)

构建二叉树:

// 通过前序遍历的数组"ABC###D##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType*arr,int* pi)
{
	if (arr[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	root->left = NULL;
	root->right = NULL;
	root->val = arr[(*pi)++];
	root->left = BinaryTreeCreate(arr, pi);
	root->right = BinaryTreeCreate(arr, pi);
	return root;
}

‘#’是NULL

疑问:数组下标的指针pi,如果传值的话,就是临时拷贝

如果传的是指针的话,因为是递归,调用递归的被调用的函数中i的改变不会改变调用该函数中的i的值,因为是值拷贝,如果还不理解的话,可以看一下关于函数值调用和址调用的区别。

销毁二叉树:

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

二叉树的销毁采用后续遍历,如果采用前序或后序会非法访问,并且释放不干净。


重头戏:中序遍历

什么是中序遍历?

就是一层一层的遍历。

上图中序遍历的结果是: 1,2,3,4

那么如何实现中序遍历呢?

可以通过队列来实现,现将根节点入队列

每个元素出队列时,打印该节点的值,并将该元素的左右子树的根节点入队列,如果节点为NULL,那么就不入队列,直至队列为空为止

这有一副流程图:

 代码:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);//初始化队列
	if(root)
		QueuePush(&q,root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);//获得队头数据
		QueuePop(&q);
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
		printf("%c ", front->val);
	}
}

注意:这里队列中的存储的元素的数据类型为,二叉树节点的指针

完成这个代码需要用到以前写过的队列的代码

如果有需要这里有链接,数据结构: 实现初阶数据结构的代码 (gitee.com),如果不会队列的话,也可以看我以前的博客写的队列。

typedef BTNode* QDataType;
typedef struct QNode
{
	struct QNode* next;
	QDataType val;
}QNode;

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

这个思路跟上面中序遍历的思路一样,都需要用到队列,不同的是,中序遍历不将NULL入队列,

而这个如果左右子树的根为NULL,仍入队列。 

// 判断二叉树是否是完全二叉树
bool 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)
			return false;
	}
	return true;
}

思路: 第一次出队列的元素为NULL时,就一直将队列中的元素一直出队列,如果全为NULL则为完全二叉树只要有一个不为NULL,那么就不是完全二叉树。

 

结语:这就是今天的分享,希望看到这篇博客的人都能有所收获。

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

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

相关文章

学习编程对英语要求高吗?

学习编程并不一定需要高深的英语水平。我这里有一套编程入门教程,不仅包含了详细的视频讲解,项目实战。如果你渴望学习编程,不妨点个关注,给个评论222,私信22,我在后台发给你。 虽然一些编程资源和文档可能…

公务员申论答题稿纸

申论答题稿纸网上不太好找,这里给大家分享两个版本,一个是在PDD上买到的稿纸样式,一种是那种考场答题样式,每个题都给你留有答题位置。第一种合适大家日常练习,比如说练字,第二种就适合答题的时候用了。 …

硬盘的分区

目录 概念 硬盘的分区 实操 创建分区 fdisk&#xff08;<2T&#xff09; 创建文件系统 挂载 自动挂载&#xff08;永久挂载&#xff09; gpt区分 swap 交换分区 如何删除已挂载的分区 概念 硬盘&#xff1a;计算机的存储设备。&#xff08;如无特殊说明&#xff0…

区间预测 | Matlab实现GRU-Attention-KDE核密度估计多置信区间多变量回归区间预测

区间预测 | Matlab实现GRU-Attention-KDE核密度估计多置信区间多变量回归区间预测 目录 区间预测 | Matlab实现GRU-Attention-KDE核密度估计多置信区间多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现GRU-Attention-KDE门控循环单元注意力…

移动端应用订阅SDK接入攻略

本文档介绍了联想应用联运移动端订阅SDK接入操作指南&#xff0c;您可在了解文档内容后&#xff0c;自行接入应用联运移动端订阅SDK。 接入前准备 1请先与联想商务达成合作意向。 2.联系联想运营&#xff0c;提供应用和公司信息&#xff0c;并获取商户id、app id、key&#…

Python编程之旅:从错误到精通的奇妙探险

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、初识Python编程的陷阱与解决方案 1. 语法错误&#xff1a;防范于未然 2. 逻辑错误&…

流媒体内网穿透/组网/视频协议转换EasyNTS上云网关如何更改密码?

EasyNTS上云网关的主要作用是解决异地视频共享/组网/上云的需求&#xff0c;网页对域名进行添加映射时&#xff0c;添加成功后会生成一个外网访问地址&#xff0c;在浏览器中输入外网访问地址&#xff0c;即可查看内网应用。无需开放端口&#xff0c;EasyNTS上云网关平台会向Ea…

shell脚本读写二进制文件

文章目录 shell脚本读写二进制文件读取二进制文件使用xxd命令使用od命令 写入二进制文件使用echo和printf 读取和修改二进制文件使用dd命令 组合使用工具 shell脚本读写二进制文件 在Shell脚本中处理二进制文件时&#xff0c;可以使用一些常用的命令和工具来读取和写入二进制数…

Leetcode刷题笔记5

76. 最小覆盖子串 76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a; 暴力枚举 哈希表 先定义left和right&#xff0c;可以在随机位置 枚举一个位置向后找&#xff0c;找到一个位置之后&#xff0c;发现这段区间是一个最小的区间之后&#xff0c…

C++ day1 作业练习

整理思维导图 定义自己的命名空间my_sapce&#xff0c;在my_sapce中定义string类型的变量s1&#xff0c;再定义一个函数完成对字符串的逆置。 #include <iostream> #include <cstring>using namespace std; namespace my_space {string s1; }void show() {cout<…

ONLYOFFICE 协作空间与 WordPress 如何集成

转载自作者&#xff1a;VincentYoung&#xff0c;略有改动 阅读本文&#xff0c;了解如何将 ONLYOFFICE 协作空间与 WordPress 进行集成。 ONLYOFFICE 协作空间是其去年新推出的产品&#xff0c;用创建虚拟办公室房间的方式&#xff0c;来组织公司内部团队成员的在线协作办公&…

FBA头程空运物流的运输时效与计费方式解析

FBA头程空运物流&#xff0c;是运用专业的货机或搭载货物的客机&#xff0c;为轻小商品提供的一种直接、迅速的配送服务。它尤其适合时间紧促的物流伙伴&#xff0c;尽管相较于其他运输方式&#xff0c;空运成本可能稍高&#xff0c;但其卓越的时效性为物流伙伴带来了极高的性价…

单片机的内存映射和重映射

内存映射 在单片机内&#xff0c;不管是RAM还是ROM还是寄存器&#xff0c;他们都是真实存在的物理存储器&#xff0c;为了方便操作&#xff0c;单片机会给每一个存储单元分配地址&#xff0c;这就叫做内存映射。 单片机的内存映射是指将外部设备或外部存储器映射到单片…

linux下的openssh简介(centos 8)

目录 1. 简介2. 安装 OpenSSH3. 配置 OpenSSH 服务器3.1 服务器配置文件配置文件的详解 3.2 安全操作——修改 SSH 端口3.3 安全操作——禁止 root 登录3.4 安全操作——密钥认证3.5 安全操作——禁止密码认证 4. 配置 OpenSSH 客户端4.0 常用命令4.0.1 ssh常用命令4.0.2 scp常…

ubuntu22部署Docker私有仓库Harbor (http https方式)

harbor日志&#xff1a;/var/log/harbor 前置安装配置 需先安装docker和docker-compose&#xff1a; 0.配置清华大学apt源并安装docker #信任 Docker 的 GPG 公钥: sudo apt-get install ca-certificates curl gnupg curl -fsSL https://download.docker.com/linux/ubunt…

遥测终端机RTU:为水利细分场景量身定制的智能化应用

在日益智能化的现代社会&#xff0c;水利行业正迎来前所未有的变革。为了更好地满足水利细分场景的需求&#xff0c;我们针对每个细分场景推出了专用遥测终端机&#xff0c;为您的水利工作带来前所未有的便捷与高效。 为一款智能化应用&#xff0c;遥测终端机的安全性也是我们…

图形学初识--纹理采样和Wrap方式

文章目录 前言正文1、为什么需要纹理采样&#xff1f;2、什么是纹理采样&#xff1f;3、如何进行纹理采样&#xff1f;&#xff08;1&#xff09;假设绘制区域为矩形&#xff08;2&#xff09;假设绘制区域为三角形 4、什么是纹理的Wrap方式&#xff1f;5、有哪些纹理的Wrap方式…

强大友好的Nginx扩展:VeryNginx

VeryNginx&#xff1a; 简化Web管理&#xff0c;增强网站防御- 精选真开源&#xff0c;释放新价值。 概览 VeryNginx是一个基于lua-nginx-module&#xff08;openresty&#xff09;的高效、友好的Nginx版本&#xff0c;专为满足现代Web应用的需求而设计。它不仅提供了强大的We…

Java | Leetcode Java题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int numDistinct(String s, String t) {int m s.length(), n t.length();if (m < n) {return 0;}int[][] dp new int[m 1][n 1];for (int i 0; i < m; i) {dp[i][n] 1;}for (int i m - 1; i > 0; …

K-means聚类模型入门介绍

K-means聚类是一种无监督学习方法&#xff0c;广泛应用于数据挖掘、机器学习和模式识别等领域&#xff0c;用于将数据集划分为K个簇&#xff08;cluster&#xff09;&#xff0c;其中每个簇的数据具有相似的特征。其基本思想是通过迭代寻找使簇内点间距离平方和最小的簇划分方式…