链式二叉树的查找,遍历(递归实现)等接口的实现

news2024/11/13 8:42:37

目录

前言:

一:二叉树的建立

(1)本文采用的二叉树表示方法

(2)手动建立一颗二叉树

二:二叉树的遍历

(1)二叉树的三种遍历方式

(2)分治思想

(3)前序遍历

 (4)中序遍历

(5)后序遍历

三:求二叉树的节点和高度(深度)

(1)求二叉树节点

①求二叉树的全部节点

②求二叉树的叶子节点

③求二叉树第k层节点的个数

(2)求二叉树的高度(深度)

四:二叉树的查找


前言:

之前我们初步的讲解了二叉树并且实现了堆这种特殊的二叉树,本次我们将实现链式二叉树的遍历(链式二叉树中非常重要的部分),查找等功能。

附初识二叉树链接:http://t.csdn.cn/pMOia

一:二叉树的建立

(1)本文采用的二叉树表示方法

①每一个节点都是一个结构体。

②每一个节点除了存储数据,还存储了自己孩子节点的地址(结构体指针)。

③如果节点没有孩子就指向空

示意图:

代码:

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	//存储左孩子的地址
	struct BinaryTreeNode* left;
	//存储右孩子的地址
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;

(2)手动建立一颗二叉树

①调用malloc( )函数申请空间,插入数据。

②将节点依次链接

③因为需要多次申请空间,插入数据,我们把这个部分封装成函数BuyNewNode( )。

代码:

//申请新节点
BTNode* BuyNewNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}

	newnode->data = x;
	newnode->left = newnode->right = NULL;
	
	return newnode;
}


void test1()
{
    //手动建立一个二叉树
	BTNode* nodeA = BuyNewNode('A');
	BTNode* nodeB = BuyNewNode('B');
	BTNode* nodeC = BuyNewNode('C');
	nodeA->left = nodeB;
	nodeA->right = nodeC;
	BTNode* nodeD = BuyNewNode('D');
	BTNode* nodeE = BuyNewNode('E');
	BTNode* nodeF = BuyNewNode('F');
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;
}

这个二叉树的图:

二:二叉树的遍历

(1)二叉树的三种遍历方式

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右
子树之前
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中
(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后

(2)分治思想

分治就是分而治之,大概意思就是将一个看似复杂的问题化成一个个简单的小问题,最后解决问题的思想,也是本文的核心。
好比一个学校要统计学生人数,可以让校长一个个去数,也可以让校长告诉年级主任,主任告诉班主任,班主任告诉宿舍长。

(3)前序遍历

 我们看这颗二叉树,如果要进行前序遍历。

先打印A,然后遍历A左子树。

打印B,遍历B左子树。

打印D,遍历D左子树。

为空,遍历D右子树。

为空,B的左子树遍历结束,遍历B的右子树。

为空,A的左子树遍历结束,遍历A的右子树。

……………………

②我们不难发现,如果要前序遍历整棵树

可以转化为先访问A后前序遍历A的左子树和右子树

前序遍历A的左子树可以转化为先访问B后前序遍历B的左子树和右子树

前序遍历B的右子树又可以转化为先访问D后前序遍历D的左子树和右子树,这样可以把一个较大的问题转化为一个个极小的问题。

代码:

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

	//打印
	printf("%c  ", root->data);
	//左子树
	PreOrder(root->left);
	//右子树
	PreOrder(root->right);
}

大家遇到这种递归不理解的话可以画一下递归展开图

 (4)中序遍历

如果要对这颗二叉树进行中序遍历。

先遍历A左子树。

遍历B左子树。

遍历D左子树。

空,打印D,遍历D右子树。

空,打印B,遍历B右子树。

空,打印A,遍历A右子树。

………………

②中序遍历整颗树,

可以转化为中序遍历A的左子树后访问A,然后中序遍历右子树

中序遍历A的左子树可以转化为中序遍历B的左子树后访问B,然后中序遍历右子树

中序遍历B的右子树又可以转化为中序遍历D的左子树后访问D,然后中序遍历右子树,这样可以把一个较大的问题转化为一个个极小的问题。

代码:

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("空  ");
		return;
	}

	//左树
	InOrder(root->left);
	//打印
	printf("%c  ", root->data);
	//右树
	InOrder(root->right);
}

递归展开图:

(5)后序遍历

如果要对这颗二叉树进行后序遍历。

先遍历A左子树。

遍历B左子树。

遍历D左子树。

空,遍历D右子树。

空,打印D,遍历B右子树。

空,打印B,遍历A右子树。

……………………

②后序遍历整颗树,

可以转化为后序遍历A的左子树和右子树后访问A

后序遍历A的左子树可以转化为后序遍历B的左子树和右子树后访问B

后序遍历B的右子树又可以转化为后序遍历D的左子树和右子树后访问D,这样可以把一个较大的问题转化为一个个极小的问题。

代码:

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("空  ");
		return;
	}
	//左树
	PostOrder(root->left);
	//右树
	PostOrder(root->right);
	//打印
	printf("%c  ", root->data);
}

递归展开图:

三:求二叉树的节点和高度(深度)

(1)求二叉树节点

①求二叉树的全部节点

思路:

①只有节点地址不为空就算一个节点。

②求整颗树节点,可以转化为A的左子树节点数加A的右子树节点数加1

A的左子树节点数可以转化为B的左子树节点数加B的右子树节点数加1

B的左子树节点数可以转化为D的左子树节点数加D的右子树节点数加1

D的左右子树都是空,B的左子树节点数为1

………………………………

代码:

//求树的节点数
int BinaryTreeSize(BTNode* root)
{
	/*if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;*/
	//更加简洁的写法
	return root == NULL ? 0 :
		BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

递归展开图:

②求二叉树的叶子节点

思路:

左右孩子都为空的节点算作一个叶子节点

②求整颗树的叶子节点,可以转化为求A的左子树叶子节点和A的右子树叶子节点

A的左子树叶子节点可以转化为求B的左子树叶子节点加B的右子树叶子节点

D的左右孩子都为空,B的左子树叶子节点为1。

…………………………

代码:

//求叶子节点
int BinaryLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if ((root->left == NULL) && (root->right == NULL))
	{
		return 1;
	}

	return BinaryLeafSize(root->left) + BinaryLeafSize(root->right);
}

递归展开图:

③求二叉树第k层节点的个数

思路:

假设k为3。

一颗树第一层节点数为1

空节点代表节点数为0

③求整颗树第3层的节点数,可以转化为求A的左子树以及右子树的第2层节点数之和

A的左子树第2层节点数,可以转化为求B的左子树以及右子树的第1层节点数之和

B左子树不为空,层数为1,节点数为1

B的右子树为空,节点数为0

………………………………

代码:

//求第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root,int k)
{
	//非法输入直接报错
	assert(k >= 1);

	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}

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

递归展开图:

(2)求二叉树的高度(深度)

思路:

空树高度为0

②一颗树根节点左右孩子均为空,高度为1

③一颗树最终的高度为左右子树中深度较大的一方加1

④求整颗树的高度可以转化为A的左右子树中高度较大的一方加1

A的左子树高度可以转化为B的左右子树中高度较大的一方加1

B的左子树高度可以转化为D的左右子树中高度较大的一方加1

D的左右孩子均为空,B的左子树高度为1

B的右子树为空树,B的右子树高度为0

取大的一方加1,A的左子树高度为2

代码:

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

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return max(BinaryTreeDepth(root->left), BinaryTreeDepth(root->right)) + 1;
}

递归展开图:

四:二叉树的查找

功能:输入要查找的数据x,返回节点的地址

思路:

假设要查找E

①如果找到返回节点地址,没找到返回空

②递归调用的时候要根据返回值来判断是否找到

如果不为空代表找到了,不需要继续查找,返回节点地址

③在整颗树查找E,先看根部是否为E,不是在A的左右子树中查找。

先看A的左子树根部是否为E,不是在B的左右子树中查找。

先看B的左子树根部是否为E,不是在D的左右子树中查找。

D的左右子树为空,返回空。

B的右子树为空,返回空。

A的左子树查找完毕,没找到,查找A的右子树。

先看A的右子树根部是否为E,不是在C的左右子树查找。

………………………………

代码:

//查找值为x的节点
BTNode* BianrtTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* leftRet = BianrtTreeFind(root->left,x);
	if (leftRet != NULL)
	{
		return leftRet;
	}

	BTNode* rightRet = BianrtTreeFind(root->right,x);
	if (rightRet != NULL)
	{
		return rightRet;
	}

	return NULL;
}

递归展开图(查找E):

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

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

相关文章

python+vue 餐饮食品安全监管投诉平台

系统主要包括个人中心、用户管理、餐饮类型管理、餐饮企业管理、案例类型管理、案例展示管理、法规分类管理、法律法规管理、在线投诉管理、查处信息管理、系统管理等功能模块。 绪论 网站的开发背景,意义和系统状况等,详细讲述了系统的用处,…

基于构效关系模型的药物设计(QSAR)

基于构效关系模型的药物设计(QSAR) 定量构效关系(QSAR,Quantitative Structure-Activity Relationship)分析是指利用理论计算和统计分析工具来研究系列化合物结构(包括二维分子结构、三维分子结构和电子结…

Latex数学公式排版

文章目录 Latex使用最佳方式:读官方文档Latex中的字符数学公式排版1.引入宏包:2.公式排版基础3.数学符号(1).希腊字母(2).指数,上下标,导数(3).分式和根式(4).关系符(5).算符(6).巨算符(7).箭头 Latex使用 最佳方式:读官方文档 The not so short intro…

案例分析:真实案例对“引用类型”的思考

在一个风和日丽的早上,我刚坐到工位上,正准备美美的享受早餐时,我的测试小兄弟杨过火急火燎的来找我说:“小米,不好了,运营童鞋反馈,咱们商城小程序金刚区的新店专区,新开的店铺无法…

mac上 qt与mysql的连接问题

经过两天的折磨和挣扎,将Mac上QT与mysql数据库链接问题的解决做一个梳理,以防忘记,并供他人借鉴; 环境版本:Mac10.14,MySQL5.7.24,Qt5.14.2 首先我重新下载安装了Qt(安装了源码&am…

Hadoop课程笔记

Hadoop笔记 nn和sn的区别 nn有inprogress,sn没有,隔一段时间sn会拉取nn上的fsi和edits进行合并然后返回给nnnn和dn 序列化和反序列化 当需要将内存中对象从一个服务器传输到另一个服务器的时候,将内存中的对象写进磁盘(序列化&am…

python知识点总结(国家一级假勤奋大学生整理)

python知识点总结 0. 持续更新~1. print不加end自动加回车自动换行2. eval()提取值3. 三双引号字符串中可以包含换行符,制表符以及其他特殊字符4. 字符串不能修改5. 用in 或 not in判断字串是否在母串中6. 网上做题print慎用‘,’会产生不该有的空格7. i…

职场规则实录(不要成全别人,恶心自己)

即使我们因为做出某种决定失去了某些东西,但这并不一定意味着这个决定是错误的。相反,很多时候,即使我们面临诸多风险,我们也要冒险去做出决定,因为在这种风险中可能存在着丰富的机会和收获。就像投资一样,就算我们的投资并没有获得预期的收益,但这并不代表这个投资是错…

Linux 性能优化大全!

性能指标 高并发和响应快对应着性能优化的两个核心指标:吞吐和延时 应用负载角度:直接影响了产品终端的用户体验 系统资源角度:资源使用率、饱和度等 性能问题的本质就是系统资源已经到达瓶颈,但请求的处理还不够快&#xff0…

【超算/先进计算学习】日报5

目录 今日已完成任务列表遇到的问题及解决方案任务完成详细笔记程序性能分析程序流程分析程序静态分析工具 understand程序性能动态分析工具 gprof-使用方式程序性能动态分析工具 gprof-输出结果详解程序性能动态分析工具 gropf-函数调用关系图程序动态分支辅助方式-计时函数其…

git版本控制

git版本控制 time:2023-04-18 版本控制 集中式版本控制 多人协作开发 创建仓库 使用当前目录作为 Git 仓库,我们只需使它初始化。 git init使用我们指定目录作为Git仓库。 git init newrepo克隆远程仓库 git clone https://gitee.com/zhang-min…

银行数字化转型导师坚鹏:银行数字化创新应用与案例分析

银行数字化创新应用与案例分析 课程背景: 很多银行存在以下问题: 不知道如何进行数字化创新? 不知道金融科技在银行业的重要应用? 不清楚银行同业的数字化创新有哪些案例? 课程特色: 用独特视角…

在C上++ -- 函数重载与引用

一、函数重载 函数重载:是函数的一种特殊情况,C允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型 不同的问题。 简单来说&#xff1a…

点云处理及三维重建软件(Point Cloud Viewer, PCV)的设计与实现

GitHub 地址:point-cloud-viewer 文章目录 使用教程以及相关工具库Step 1 搭建环境Step 2 使用Cmake构建工程Step3 使用VS 编写code并编译执行 点云处理及三维重建软件(PCV)的设计与实现一, 软件总体设计1.1 软件设计流程需求分析总体设计技术选型详细设…

【RestFul系列】RestFul学习笔记

目录 一、REST定义 二、REST架构的主要原则 三、RESTful介绍 1、资源(Resources) 2、 表现层(Representation) 3、 状态转化(State Transfer) 四、RESTful的使用 1、RESTful资源操作 2、接口示例&…

verilog设计实现8b-10b编码器包括3b4b,5b6b 及modelsim仿真

下面是8b10b编码器的设计步骤。 确定数据输入和输出接口。例如,您需要确定8位并行数据输入和10位串行数据输出。 计算数据带宽。这与芯片中可用的时钟速度密切相关。 选择编码表。根据应用选择最佳编码表,8b10b编码器至少应使用一张编码表。详细了解和选择编码表有利于改善编…

图的存储及基本操作总结(邻接矩阵、邻接表)及C/C++代码实现

文章目录 前言一、邻接矩阵1.概念2.图像示例3. 代码实现注意邻接矩阵的特点 二、邻接表1.概念2.图像示例3.代码实现邻接表的特点 前言 图是一种比较复杂的数据结构,每个结点之间可以有多种关系。 所以,一个图可以呈现出千奇百怪的形式。 对于不同的形式…

使用FFMPEG库将YUV编码为H264

准备 ffmpeg 4.4 p准备一段yuv420p的格式的视频原始数据 这里我们使用命令直接提取 ffmpeg -i .\beautlWorld.mp4 -pixel_format yuv420p -s 1280x720 yuv420p_1280x720.yuv 编码流程 大致可以分为以下几步: 1.初始化编码器并设置参数 2.初始化AVPacket和AVFr…

【Java基础 1】Java 环境搭建

🍊 欢迎加入社区,寒冬更应该抱团学习:Java社区 📆 最近更新:2023年4月22日 文章目录 1 java发展史及特点1.1 发展史1.2 Java 特点1.2.1 可以做什么?1.2.2 特性 2 Java 跨平台原理2.1 两种核心机制2.2 JVM…

Activiti入门

目录 一、了解工作流 1、什么是工作流 2、工作流引擎 3、常见工作流引擎 4、Activiti7概述 4.1、Activiti介绍 4.2、建模语言BPMN 4.3、Activiti使用流程 一、了解工作流 1、什么是工作流 工作流(Workflow),就是通过计算机对业务流…