二叉树的一些练习题

news2024/11/28 3:41:54

前言

二叉树的简单题目,通过画栈帧图去理解过程。画一画,走一走递归过程,理解会更加深刻。

二叉树练习题

  • 前言
  • 二叉树的创建
    • 二叉树先序遍历创建
      • PreCreat
    • 二叉树层次创建
      • LevelCreat
  • 二叉树的销毁
    • BinaryTreeDestory
  • 二叉树求节点个数
    • BinaryTreeSize
  • 二叉树叶子节点个数
    • BinaryTreeLeafSize
  • 二叉树求高度
    • BinaryBTHeight
  • 二叉树第k层的节点数
    • BinaryTreeLevelKSize
  • 检测两颗二叉树是否完全相同
    • isSameTree
  • 另一棵树的子树
    • isSubtree
  • 二叉树的翻转
    • invertTree

二叉树的创建

二叉树先序遍历创建

根据前序遍历的序列"ABD##E#H##CF##G##"

在这里插入图片描述

PreCreat

调用函数,需谨记形参的改变不能改变实参。在对字符串遍历的过程,需要确保每次栈帧中,对应下标是正确的,那么传参是必须传址调用。

看不懂的话,属实没辙,需要自己画一画。每个栈帧相当于一个节点,只有函数返回后,才能把整个树串起来。
你可以这样去理解递进过程:(看实际的二叉树)递进过程做的事情是每个节点找到了自己所在的位置。
返回过程:父子节点手拉手,心连心(皮一皮)

如何看递归展开图:跟着箭头和代码。

在这里插入图片描述

 struct BTNode
{
	int val;
	struct BTNode* left;
	struct BTNode* right;
};

struct BTNode* PreCreat(char* str, int* pi)
{
	if (str[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	struct BTNode* root = (struct BTNode*)malloc(sizeof(struct BTNode));
	root->val = str[(*pi)++];
	root->left = PreCreat(str, pi);
	root->right = PreCreat(str, pi);
	return root;
}
int main()
{
	char str[] = "ABD##E#H##CF##G##";
	int i = 0;
	struct BTNode* root = PreCreat(str, &i);
	return 0;
}

二叉树层次创建

层次创建二叉树。给定层次遍历的结果,将之变成对应的二叉树。
需要用到队列,哪个过程符合先进先出的原则呢?取父节点的过程。
层次遍历结果的字符串为:1234\01、2、3这三个节点很容易串起来,关键在4节点怎么找到它的父节点是2节点,假设后面还有节点,怎么保证后面的每个节点都能对应的上相应的父节点。
通过队列来保证,#表示NULL
在这里插入图片描述

根节点先入队,进入循环。循环结束条件:当前字符串不为'\0'。出队的节点当作父节点,并且按顺序链接左孩子、右孩子并把这两个孩子按顺序入队。重复

在这里插入图片描述

下列代码为伪代码:未把队列的接口函数给出。栈和队列需要的话自己去这个博客里cv一下。

LevelCreat


struct BTNode
{
	char val;
	struct BTNode* left;
	struct BTNode* right;
};

typedef struct BTNode* QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	QDataType data;
	struct QListNode* next;

}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;

struct BTNode* AollocBTNode(char val)
{
	struct BTNode* root = (struct BTNode*)malloc(sizeof(struct BTNode));
	root->val = val;
	root->left = NULL;
	root->right = NULL;
	return root;
}
struct BTNode* LevelCreat(char* str)
{
	Queue q;
	QueueInit(&q);
	int i = 0;
	struct BTNode* root = AollocBTNode(str[i++]);
	struct BTNode* Reroot = root;

	QueuePush(&q, root);
	//1234
	while (str[i] != '\0')
	{
		root = QueueFront(&q);//取队头元素
		QueuePop(&q);//弹出队头元素
		if (str[i] != '#')
		{
			root->left = AollocBTNode(str[i++]);
			QueuePush(&q, root->left);//左孩子入队
		}
		else
			i++;
		if (str[i] != '\0')
		{
			if (str[i] != '#')
			{
				root->right = AollocBTNode(str[i++]);
				QueuePush(&q, root->right);//右孩子入队
			}
			else
				i++;
		}
	}
	QueueDestroy(&q);
	return Reroot;
}
int main()
{
	char str[] = "ABCDEFG###H";
	struct BTNode* root = LevelCreat(str);
	return 0;
}

二叉树的销毁

什么时候将空间释放?肯定是最后一次经过该节点的时候,那么得选择后序遍历。
(前序:第一次经过某个节点,中序:第二次经过某个节点,后序:最后一次经过某个节点)
不管是第一次,还是第二次经过某个节点,都不能释放,因为它递归还没执行到最后,如果先把节点释放了,它继续进行递归就会找不到它的孩子们了。
下面给了两种形式的,一个是二级指针形式,一个是一级指针形式的。
二级指针形式是传址调用,释放后对应的指针(指向每个节点的指针)都会置为NULL
一级指针形式是传值调用,释放后对应的指针仍然指向该地址,但没有权限访问。为了避免非法访问,调用完该函数后,主函数中还需要执行root = NULL;

BinaryTreeDestory

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
		return;
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}
//主函数调用BTDestory后还需要执行 root = NULL;
void BTDestory(BTNode* root)
{
	if (root == NULL)
		return;
	BTDestory(root->left);
	BTDestory(root->right);
	free(root);
}
//伪代码:
int main()
{
     BTDestory(root);
     root = NULL;
}

二叉树求节点个数

看图理解。 函数中的+1这个1是当前栈帧中是否有节点

在这里插入图片描述

BinaryTreeSize

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

二叉树叶子节点个数

通过递归去走完每一个节点,走到特定的节点(叶子节点),需要带回返回值。
和上面一题差不多。叶子节点:没有左孩子也没有右孩子的节点。
需要判断:只有是叶子节点的时候才返回1,当为NULL时返回0,其它情况返回之前的结果。
可以像我上面画的图一样,自己动手画一画,去理解其中过程。

BinaryTreeLeafSize

int BinaryTreeLeafSize(BTNode* root)
{
	if (root && root->left == NULL && root->right == NULL)
		return 1;
	if (root == NULL)
		return 0;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

二叉树求高度

随便给你一颗树,你怎么求这个树的高度呢?
分治思想:在某节点,你知道该节点的左子树、右子树的高度,取其中最大值,再+1就能得到当前节点总共的高度。每一棵子树都是这样得到的。
在代码具体实现的时候,是先知道最下面的高度,返回后才能知道上面节点的高度。

在这里插入图片描述
在这里插入图片描述

BinaryBTHeight

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

二叉树第k层的节点数

假设k为3,根据下图所示,当k==1时,就是我们所要找的层,因此在这里就可以返回了。

在这里插入图片描述

如果k为5,不会到k==1,会先碰到NULL返回


在这里插入图片描述

BinaryTreeLevelKSize

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);
}

检测两颗二叉树是否完全相同

LeetCode.100.相同的树

写的时候要直接写能得出结果的,就是说当两棵树不相同的时候,直接返回false,而不判断是否相同,因为当前节点相同了,但是它的孩子不一定相同。不相等时直接返回false简单粗暴。

会出现下面两种情况:①一棵树为NULL,另一棵为非空,这种情况是false。②两棵树的节点都为NULL才能说是true。条件写的时候②要放在上面,①要放在下面。因为if(p == NULL || q == NULL)这个条件包含了p == NULL && q == NULL这种情况

isSameTree

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
    return true;
    if(p == NULL || q == NULL)
    return false;
    if(p && q && p->val != q->val)
    return false;
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

另一棵树的子树

LeetCode.572.另一棵树的子树
在这里插入图片描述

还记的上面一题写的相同的树吗?在这一题可以使用吗?当然可以,而且这样写会非常简单。
root这棵树去遍历它的每个节点,每个节点都当作新子树的根subRoot这棵树判断是否为相同的树。相同了就可以返回true,这种情况下是不需要遍历完所有的节点。如果遍历完所有的节点,都没相同的子树才能说false。因此返回的逻辑关系是“或”

isSubtree

 bool isSameTree(struct TreeNode* p, struct TreeNode* q)
 {
	 if (q == NULL && p == NULL)
		 return true;
	 if (q == NULL || p == NULL)
		 return false;
	 if (q->val != p->val)
		 return false;
	 return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
 }
 bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
	 if (root == NULL)
		 return false;
	 if (isSameTree(root, subRoot))
		 return true;
	 return isSubtree(root->left, subRoot) ||isSubtree(root->right, subRoot);

}

二叉树的翻转

找到规律就很简单。每个节点的孩子交换,就能完成翻转。
在这里插入图片描述

invertTree

struct TreeNode* invertTree(struct TreeNode* root)
{
    if (root == NULL)
        return NULL;
    struct TreeNode* tmp = root->left;
    root->left = root->right;
    root->right = tmp;
    invertTree(root->left);
    invertTree(root->right);
    return root;
}

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

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

相关文章

关于C语言输入输出的逗号问题(小细节)

C语言的输入输出必须要遵循scanf和printf的格式&#xff0c;就是你是什么格式你就要输入什么。 一、输入问题 #include <stdio.h> int main() { int a,b;scanf("%d,%d",&a,&b);printf("ab%d",ab);return 0; } 这个程序我们可以看到它运行…

【雷达开源数据集 | 代尔夫特数据集(VOD),4D雷达、激光雷达和相机数据】

本文编辑&#xff1a;调皮哥的小助理 1、介绍 代尔夫特视图 &#xff08;VoD&#xff09; 数据集是一种新颖的汽车数据集&#xff0c;包含 8600 帧同步和校准的 64 层 LiDAR、&#xff08;立体&#xff09;摄像头和 31D 雷达数据&#xff0c;这些数据是在复杂的城市交通中采集…

《编码 - 代码分析》代码结构分析

1 代码结构分析概述 在编写代码时&#xff0c;要求要结构清晰、接口简单。如果代码结构过于复杂&#xff0c;会带来很多问题&#xff1a;代码很难被理解&#xff0c;不方便编写测试用例&#xff0c;容易隐藏错误&#xff0c;出现问题难以定位&#xff0c;修改代码容易产生新的…

基于Java+SpringBoot+vue+elementui的校园文具商城系统详细设计和实现

基于JavaSpringBootvueelementui的校园文具商城系统详细设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录基…

前端学习框架

一&#xff0c;学习路线图 https://roadmap.sh/ 二&#xff0c;学习资源整理 https://developer.mozilla.org/zh-CN/docs/Learn // 学习web 的各种资源网站 三&#xff0c;知识整理 将前端分为三步走 JavaScript 语言&#xff08;模块一&#xff09;CSS 和 HTML&#x…

【Latex】1.Latex环境的安装与配置

文章目录前言1 安装环境2. 下载texstudio前言 LaTeX&#xff08;LATEX&#xff0c;音译“拉泰赫”&#xff09;是一种基于ΤΕΧ的排版系统&#xff0c;由美国计算机学家莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在20世纪80年代初期开发&#xff0c;利用这种格式&a…

sqlmap之绕过云锁waf

sql注入不会绕过WAF&#xff1f;关注我&#xff0c;让我带你由简入难实战各个WAF&#xff0c;前天我们实战了安全狗的WAF&#xff0c;今天先来看看比它难度更高的云锁WAF&#xff0c;你会绕吗&#xff1f;看我带你将它拿下 目录 一&#xff1a;环境配置 1.云锁WAF 2.sqli-l…

本地数仓项目(二)——搭建系统业务数仓详细流程

1 说明 本文基于《本地数据仓库项目(一)——本地数仓搭建详细流程》业务数据&#xff0c;在本地搭建系统业务数仓。 根据模拟sql脚本生成业务数据&#xff0c;依次执行生成业务数据即可。 sql脚本提供如下 链接&#xff1a;https://pan.baidu.com/s/1WX3xVMQvAApSUZMobWLiLQ…

Vue-cli创建项目的目录结构与子父组件之间的通信

一、Vue脚手架的使用1、通过命令行使用vue-cli的指令创建&#xff1a;vue init webpack 项目名称build文件夹&#xff1a; 用于webpack打包配置文件夹config文件夹&#xff1a;与webpack相关的配置文件夹|——- index.js&#xff1a;可以改变项目默认的端口号node_moudles&…

Vue3/ Vite 的使用介绍 、Vite 方式工作流程 与 传统方式工作流程区别

一. Vite 的使用介绍 优势: &#x1f4a1;极速的服务启动 使用了原生的 ESM文件 无需打包 ⚡️ 轻量快速的热重在 始终极快的模块热重载 &#x1f6e0;️丰富的功能 对于 typescript jsx css 等支持开箱即用 &#x1f4e6; 等等 二.Vite 方式工作流程 与 传统方式工作流程…

千万不要把Request传递到异步线程里面!有坑!

前几天在网上冲浪的时候看到一篇技术文章&#xff0c;讲的是他把一个 request 请求传递到了线程池里面&#xff0c;然后遇到了一个匪夷所思的情况。 他写了这篇文章&#xff0c;把自己针对这个问题的探索过程分享了出来&#xff1a; 《springboot 中如何正确的在异步线程中使用…

ACL命名实验

目录 一.ACL命名实验 1.实验要求&#xff1a; 2.配置全网通 配置主机地址 3.根据实验要求配置ACL 4.测试完成后再根据下一次要求继续配置 一.ACL命名实验 实验图片 1.实验要求&#xff1a; 要求 全网通 服务器上配置 web 服务和 ftp 服务 配置 命名的acl 列表 …

_Linux 进程信号-信号保存篇

文章目录前言阻塞信号1. 信号常见概念2. 在内核中的表示信号处理过程3. sigset_t4. 信号集操作函数sigprocmasksigpending5. 测试与验证实验一实验二实验三前言 上篇文章&#xff08;链接: _Linux 进程信号-基础篇&#xff09;我们了解了信号的基础概念以及信号如何发送的。 …

强化学习_06_pytorch-PPO实践(Pendulum-v1)

一、PPO简介 TRPO(Trust Range Policy Optimate)算法每一步更新都需要大量的运算&#xff0c;于是便有其改进版本PPO在2017年被提出。PPO 基于 TRPO 的思想&#xff0c;但是其算法实现更加简单。TRPO 使用泰勒展开近似、共轭梯度、线性搜索等方法直接求解。PPO 的优化目标与 T…

可观测性--数据源

文章目录监控数据来源端上访问应用程序业务监控基础设施可观测性核心概念日志&#xff08;Logging&#xff09;统计指标&#xff08;Metrics&#xff09;链路追踪&#xff08;Tracing&#xff09;三者之间关系监控数据来源 我们一般讲的数据观测&#xff0c;其实观测的就是从发…

【Linux】计算机软硬件体系结构

文章目录冯诺依曼体系结构操作系统(Operator System)什么是操作系统为什么要有操作系统操作系统是怎么实现管理的系统调用接口和库函数总结冯诺依曼体系结构 谈到计算机的硬件结构&#xff0c;第一个想到的必然是经典的冯诺依曼体系结构&#xff1a; 我们常见的计算机&#xf…

在购买低代码产品时,源码是必需的吗?

编者按&#xff1a;企业在采购软件或者平台时&#xff0c;到底需不需要源码&#xff1f;本文分析了源码交付的对于不同规模和情况企业的意义&#xff0c;并介绍了源码交付的低代码平台。关键词&#xff1a;源码交付&#xff0c;可视化设计&#xff0c;私有化部署&#xff0c;多…

数据上线:首届6G智能无线通信系统大赛OPPO赛道评测正式开启

12月26日&#xff0c;首届6G智能无线通信系统大赛——面向小样本条件场景自适应及在线更新需求的无线AI设计赛题已经正式上线&#xff0c;数据集也已经在1月3日正式上线啦&#xff0c;评测同步开启&#xff0c;快来打擂冲榜&#xff01; 文末还将揭晓本赛题专属活动&#xff0…

图像锐化处理之一阶微分算子

图像锐化是通过增强图像的边缘和细节来提高图像的清晰度的操作。这种操作通常用于将模糊或不清晰的图像改进为更清晰的图像。由于微分是对函数局部变化率的一种描述&#xff0c;因此图像锐化算法的实现可基于空间微分。 一阶微分算子 对任意一阶微分的定义必须满足两点&#xf…

采用热电偶温度传感器实现超高精度温度跟踪控制的解决方案

摘要&#xff1a;针对温度跟踪控制中存在热电堆信号小致使控制器温度跟踪控制精度差&#xff0c;以及热电阻形式的温度跟踪控制中需要额外配置惠斯特电桥进行转换的问题&#xff0c;本文提出相应的解决方案。解决方案的核心是采用一个多功能的超高精度PID控制器&#xff0c;具有…