详解树与二叉树的概念,结构,及实现(下篇)

news2024/11/22 16:29:15

目录

一, 二叉树链式实现

1. 前置说明

2. 二叉树遍历(主打的就是一个分治思想)

2. 1 前序遍历

2. 2 中序遍历

2. 3 后序遍历

2. 4 层序遍历

3. 二叉树结点个数及高度

3. 1 二叉树节点个数

3. 2  二叉树叶子节点个数

3. 3 二叉树第K层的节点个数

3. 4 二叉树查找为X的节点

3. 5 二叉树的深度

 二,二叉树简单算法题

1. 965. 单值二叉树

2.100. 相同的树 

3. 572. 另一棵树的子树 

结语


一, 二叉树链式实现

1. 前置说明

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

// 结点创建
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

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

BTNode* CreatBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');
	BTNode* nodeG = BuyNode('G');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeB->right = nodeE;
	nodeC->left = nodeF;
	nodeC->right = nodeG;
	

	return nodeA;
}

构建的二叉树如图:

 

2. 二叉树遍历(主打的就是一个分治思想)

分治思想:将一棵大树分成多个小树。

2. 1 前序遍历

思路:分治思想,先根,再左孩子,最后右孩子

以上面我们所创建的二叉树,来进行前序遍历结果为:

A   B   D  NULL  NULL   E   NULL  NULL    C    F  NULL  NULL   G   NULL   NULL 

代码实现如下:

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

	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

如果还是怎么清楚可以画递归图(建议多画几次):

最后一层补充: 

2. 2 中序遍历

思路:分治思想,先左孩子,后根,最后右孩子。

结果: NULL   D  NULL  B  NULL   E  NULL   A   NULL  F   NULL   C   NULL    G     NULL

 代码:

// 二叉树中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}

 如果思路不清楚,可以按照上面的递归方法进行画图。 

2. 3 后序遍历

 思路:分治思想,先左孩子,后右孩子,最后根。

结果: NULL   NULL   D    NULL   NULL  E    B   NULL   NULL   F   NULL   NULL  G   C   A

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

		PreOrder(root->left);
		PreOrder(root->right);
		printf("%c ", root->data);
	}
}

这里我们已经学完了前中后序遍历,我们会发现三种遍历法就是遍历根的时机不同造成的。 

2. 4 层序遍历

层序遍历思路比较难想到,这里需要用到队列知识,需要队列的基本操作。

思路如下:

代码:

void LevelOrder(BTNode* root)
{
	assert(root);
	QUE my_room;
	QueueInit(&my_room);
	QueuePush(&my_room, root);
	// 因为循环条件是队列为空
	while (!QueueEmpty(&my_room))
	{   // 导出队头并出列
		BTNode* front = QueueFront(&my_room);
		printf("%c ", front->data);
		QueuePop(&my_room);
		// 开始录入孩子数据
		if (front->left)
		{
			QueuePush(&my_room, front->left);
		}
		if (front->right)
		{
			QueuePush(&my_room, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&my_room);
}

3. 二叉树结点个数及高度

3. 1 二叉树节点个数

  思路:换一种思路,  假设我们是校长,我们要统计全校师生人数,那我会打电话给每个学院的主任他们几个主任统计的人数加起来,再加上校长本人不就是全校人数了嘛;而学院主任又会打给辅导员,让他们统计各班的人数再加上他自己,通过这样的方法不断细化,直到不能再细分为止。

以上类推可知,统计二叉树我们就只要记录每个左右孩子+ 父亲的人数即可。

代码如下:

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

3. 2  二叉树叶子节点个数

 思路:如果一个根,其左右孩子都位空,那么就是叶子节点。

代码如下:


// 二叉树叶子节点个数
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);
}

3. 3 二叉树第K层的节点个数

 思路:在K = 1的时候,返回1即可,然后每个根统计左右孩子在K层的节点个数。

 

3. 4 二叉树查找为X的节点

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

3. 5 二叉树的深度

思路: 从根出发,我们只要保留左右孩子所返回的深度,取较大的深度然后加1(每个小树的根层)

代码如下:

// 二叉树的深度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0; 
	}
	int BTD_left = BinaryTreeDepth(root->left);  // 用于保存比较,避免重新遍历
	int BTD_right = BinaryTreeDepth(root->right);
	return BTD_left > BTD_right ? BTD_left + 1 : BTD_right + 1;
}

 

 二,二叉树简单算法题

1. 965. 单值二叉树

 

思路:从根开始判断左右孩子,是否同根相同。如果相同,返回True;反之false。 返回值必须左右孩子都为True才能表明两边根和孩子都相同。

代码如下:

bool isUnivalTree(struct TreeNode* root){
  if(root == NULL)
  return true;
  if(root->left && root->left->val != root->val)
  return false;
  if(root->right && root->right->val != root->val)
  return false; 

   return isUnivalTree(root->left) && isUnivalTree(root->right);
}

 


2.100. 相同的树 

 

 思路:两树同时遍历,如果不相同返回False; 

代码如下:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if	(p == NULL && q == NULL)
    return true;
    if(p  == NULL || q == NULL)
    return false;

    if(p->val != q->val)
    return false;

    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

 


3. 572. 另一棵树的子树 

这个题是相同的树的一道变形题。

  • 相同点:当根同subRoot树的根相同,则进入判断是否是相同的树这道题的逻辑。
  • 不同点:是从一棵大树中寻找,中间可能存在多个目标子树,也可能没有,那么每一个根都可能是,所以需要遍历大树,一旦找到直接返回true,毕竟题目只要求存在。 

代码如下:

bool isSymmetricSubTree(struct TreeNode* root1, struct TreeNode* root2)
{
   // 第一种, 两树全为NULL
   if(root1 == NULL && root2 == NULL)
   return true;
   // 第二种, 一树,为空,则不相等
   if(root1 == NULL || root2 == NULL)
   return false;
   // 第三种, 数值不相等,则不相等。
   if(root1->val != root2->val)
   return false;
   // 如果都不是则,这一组满足,判断下组,
   return isSymmetricSubTree(root1->left, root2->left)
          && isSymmetricSubTree(root1->right, root2->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root == NULL || subRoot == NULL)
    return false;

    if(isSymmetricSubTree(root, subRoot))
    return true;

    return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}

 

 

结语

本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。

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

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

相关文章

数字信号处理技术(三)自适应噪声完备集合经验模态分解(CEEMDAN)-Python代码

本文仅对自适应噪声完备集合经验模态分解(CEEMDAN)的原理简单介绍和重点介绍模型的应用。 1. CEEMDAN原理 CEEMDAN(Complete Ensemble Empirical Mode Decomposition with Adaptive Noise)的中文名称是自适应噪声完备集合经验模…

一文全解经典机器学习算法之支持向量机SVM(关键词:SVM,对偶、间隔、支持向量、核函数、特征空间、分类)

文章目录 一:概述二:间隔与支持向量三:对偶问题(1)什么是对偶问题(2)SVM对偶问题(3)SMO算法 四:核函数(1)核函数的概述和作用&#xf…

车载应用生态:小程序容器技术成为保障安全的有力措施

随着智能交通和车联网技术的快速发展,越来越多的车载应用程序(APP)进入人们的视野,从而推动了车载业务生态的不断发展。然而,车载应用程序的安全问题也引起了人们的广泛关注。为此,小程序容器技术作为一种有…

4.24每日一练

题目 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 来源:力扣(LeetCode) 链接:https://leetcod…

毕业设计 医学图像阅读器 DICOM CT MRI 阅读器 三维重建 可视化编程技术及应用

一、 概述 此系统实现了常见 VTK 四视图,实现了很好的 DICOM 图像显示,可用于 DICOM 超声 X线 CT MR 三维重建 拾取像素值 窗宽 窗位 像素,距离测量,角度测量,提供源码; 并且通过三维重建实现可视化。使用…

客快物流大数据项目(一百一十七):网关 Spring Cloud Gateway

文章目录 网关 Spring Cloud Gateway 一、简介 1、功能特性

OSCP-Exfiltrated(Subrion、exiftool提权)

目录 扫描 WEB 提权 其他方法 扫描 WEB 添加 host信息 访问了该网站,并立即注意到该网站上制作的CMS(Subrion CMS)。

机器学习笔记之密度聚类——DBSCAN方法

机器学习笔记之密度聚类——DBSCAN方法 引言基本思想概念介绍算法过程完整算法描述 DBSCAN \text{DBSCAN} DBSCAN的优点和缺陷 引言 本节将介绍密度聚类—— DBSCAN \text{DBSCAN} DBSCAN方法。 对于其他聚类任务的笔记: K-Means \text{K-Means} K-Means聚类算法&…

PXE高效批量网络装机

PXE 定义 PXE(预启动执行环境,在操作系统之前运行)是由Intel公司开发的网络引导技术,工作在client /server模式,允许客户机通过网络从远程服务器下载引导镜像,并加载安装文件或者整个操作系统。 具备以下三个优点 1 规模化: 同时…

高通 Android 12 framework添加自定义按键上报应用层

Android下添加新的自定义键值和按键处理流程 首先分析下Android下怎么添加新的自定义键值。在Android的原生系统中键值默认情况下是92个,从0-91;一般情况下,这些键值是够用的,但是如果想扩充的话,还是需要添加新的键值…

Linux系统应用编程(五)Linux网络编程(上篇)

本篇主要内容: Linux系统应用编程(五)Linux网络编程(上篇)一、网络基础1.两个网络模型和常见协议(1)OSI七层模型(物数网传会表应)(2)TCP/IP四层模…

详解C语言string.h中常用的14个库函数(一)

我计划讲解C语言string.h这个头文件中,最常用的14个库函数。为了让大家更加深入的理解这些函数,部分函数我会模拟实现。篇幅所限,如果文章太长了,可能会较难坚持读完,所以我会分几篇博客来讲述。本篇博客主要讲解的函数…

FPGA时序约束(三)时序约束基本路径的深入分析

系列文章目录 FPGA时序约束(一)基本概念入门及简单语法 FPGA时序约束(二)利用Quartus18对Altera进行时序约束 文章目录 系列文章目录前言基本时序路径时钟偏差寄存器到寄存器(reg2reg)建立时间余量保持时…

PHP实现以数组var_dump,array_combine等函数的方法功能举例

目录 前言 一、什么是数组 二、把两个数组合并成一个数组 1.1运行流程(思想) 1.2代码段 1.3运行截图 三、自动创建数组的一个案例 1.1运行流程(思想) 1.2代码段 1.3运行截图 前言 1.若有选择,可实现在目录里…

小朋友排队

[蓝桥杯 2014 省 B] 小朋友排队 题目描述 n n n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是 0 0 0。 如果某个小朋友…

Python 学习曲线 从 Python 新手到 Pro

Python 学习曲线 从 Python新手到 Pro 使用代码片段介绍: Python 是世界上最通用和使用最广泛的编程语言之一,以其简单性、可读性和多功能性而闻名。 在本文中,我们将探讨一系列示例场景,其中代码由具有三个不同专业知识水平的程序…

文件系统和动静态库

目录 再识文件属性 查看文件属性的原理 初识inode 了解磁盘 什么是磁盘 磁盘的结构 磁盘的存储结构 CHS寻址 磁盘的逻辑结构 使用LBA地址的意义 理解文件系统 页框和页帧 分治思想管理 Linux ext2文件系统 软硬链接 软链接 硬链接 文件的三个时间 动静态库 …

java运行python脚本,待完善版

参考资料: windows下调用CMD运行方式 兼容linux/windows,同步异步方式 指定特殊运行环境的运行(如:anaconda运行环境) 整合以上三种方式终极版源码 相关内容: 调用python脚本传参说明 如果不传参数,python脚本可以随意写,比如:

【课程介绍篇】C/C++后台开发岗位技能知识树

1 C/C后台开发岗位技能知识树 2 Linux C/C后台架构开发 成长体系课程 3 C技术历史更新 https://www.0voice.com/uiwebsite/html/courses/

《UVM实战》学习笔记——第七章 UVM中的寄存器模型2——期望值/镜像值、自动/显示预测、操作方式

文章目录 前言一、寄存器模型对DUT的模拟1.1 期望值和镜像值1.2 常见操作对期望值和镜像值的影响 二、prediction分类2.1 自动预测2.2 显式预测 三、访问寄存器方式四、mem和reg的联系和差别五、内建built_in sequence5.1 寄存器模型内建序列5.2 存储器模型内建序列5.3 禁止域名…