数据结构-二叉树力扣题
目录
1.相同的树
2.二叉树中查找值为x的节点
3.单值二叉树
4.对称二叉树
5.二叉树的前序遍历
6.另一颗树的子树
层序遍历:
7.二叉树遍历
8.判断二叉树是否是完全二叉树
一个特殊的性质:
1.相同的树
题目链接:力扣(LeetCode)
思路:
这道题我们可以把它分为三个子问题,分别是:根与根、左子树与左子树、右子树与右子树之间的比较,而且最好是用前序的方式,即先比较根,要是根都不相等,后面的就不用比较了,而子树也可以在再分为根、左子树、右子树......直到不能分为止,所以每次递归调用:如果根的值不相等,就返回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);
}
2.二叉树中查找值为x的节点
这道题是延续我们上节内容
思路:
这道题思路很简单,也是分为:查找根、左子树、右子树三个子问题,先找根的值,如果相等就返回根节点,如果不等,继续找左子树,如果相等,返回左子树的节点,如果不相等,继续找右子树,如果相等,返回右子树节点,如果不等,则继续上面过程。
但是这道题我们要求返回的是节点,所以在某次递归中,如果找到了节点,就要先把它保存下来,然后再返回到这次递归的上一级,如果不保存,那节点极大可能返回不到函数外面。总而言之,递归是一级一级往上返回的,如果递归有返回值,每次递归都要确保能接收到下一次递归返回的值。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail\n");return NULL;}node->left = NULL;node->right = NULL;node->data = x;return node;
}BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
//二叉树中查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == 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;
}
int main()
{BTNode* root = CreatBinaryTree();BTNode* p = BinaryTreeFind(root, 3);printf("%d\n", p->data);return 0;
}
3.单值二叉树
题目链接:力扣(LeetCode)
思路:
这道题,我们可以先比较根节点和左子树的值,然后比较根节点和右子树的值,当左子树不为空且它和根节点的值不相等时,返回false,当右子树不为空且它和根节点的值不相等时,返回false,当左右子树都和根节点相等时,继续递归,直到root==NULL,返回true,这时,true&&true=true,返回true。
代码如下:
bool isUnivalTree(struct TreeNode* root) {if(root==NULL){return true;}if(root->left!=NULL&&root->val!=root->left->val){return false;}if(root->right!=NULL&&root->val!=root->right->val){return false;}return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
4.对称二叉树
题目链接:力扣(LeetCode)
思路:
这道题默认有一个根节点,该节点不用比较,所以我们可以比较根节点下的左子树和右子树,这就需要两个参数,我们可以把左子树的根节点和右子树的根节点作为左根和右根传给函数_isSymmetric(),然后把左根的左子树和右根的右子树比较,左根的右子树和右根的左子树比较,如果不等,返回false,如果相等,继续递归,直到全部为空,返回true,true&&true=true,返回true。
代码如下:
bool _isSymmetric(struct TreeNode*leftRoot,struct TreeNode*rightRoot){//全部为空if(leftRoot==NULL&&rightRoot==NULL){return true;}//只有一个为空if(leftRoot==NULL||rightRoot==NULL){return false;}//全不为空if(leftRoot->val!=rightRoot->val){return false;}return _isSymmetric(leftRoot->left,rightRoot->right)&&_isSymmetric(rightRoot->left,leftRoot->right);}
bool isSymmetric(struct TreeNode* root) {return _isSymmetric(root->left,root->right);
}
5.二叉树的前序遍历
题目链接:力扣(LeetCode)
思路:
这道题看起来很简单,只需要按照根、左子树、右子树的顺序依次访问即可,但是有很多坑:
1. 需要自己malloc数组,但是数组大小不知道,所以还要写一个函数用来计算二叉树大小
2. malloc数组之后就不能在原函数中写递归了,否则,每次递归都会malloc一个空间,所以又得专门写一个递归的函数。
3.二叉树节点的值要存储到数组中,必然要使用下标,但是下标在调用中不能传值,否则每次递归调用后,栈帧销毁,栈帧中形参i++后的值不能返回上一级递归,往数组中存放数据就会出错。
下面来看一个经典的错误:
int TreeSize(struct TreeNode* root){return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;}void _preorderTraversal(struct TreeNode* root, int*a,int i){if(root==NULL){return;}a[i++]=root->val;_preorderTraversal(root->left,a,i);_preorderTraversal(root->right,a,i);}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {*returnSize=TreeSize(root);int*a=(int*)malloc(*returnSize*sizeof(int));int i=0;_preorderTraversal(root, a,i);return a;
}
可以看到,下面用例执行错误了:
为什么呢?
画一下递归调用图就知道了:
所以以后在递归时如果需要传下标,最好传地址,用指针接收。
正确代码如下:
int TreeSize(struct TreeNode* root){return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;}void _preorderTraversal(struct TreeNode* root, int*a,int* pi){if(root==NULL){return;}a[(*pi)++]=root->val;_preorderTraversal(root->left,a,pi);_preorderTraversal(root->right,a,pi);}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {*returnSize=TreeSize(root);int*a=(int*)malloc(*returnSize*sizeof(int));int i=0;_preorderTraversal(root, a,&i);return a;
}
6.另一颗树的子树
思路:
这道题本质还是找两颗相同的树,只不过一个树必须得是另外一个树的子树,那我们可以复用上文中“相同的树”的代码,然后遍历找到第一棵树的所有子树与第二棵树比较,如果不同,继续遍历,只要左右子树中有一个子树与第二颗树相同就返回true。
题目链接:力扣(LeetCode)
代码如下:
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);
}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);
}
在讲下一道题之前我们先来学习一下二叉树的层序遍历。
层序遍历:
二叉树的层序遍历是一层一层往下走的,我们可以用队列来实现它,用队列保存二叉树每一层的节点,先让根节点入队,保存并打印根节点的值,然后根节点出队,让它的左右孩子入队,相当于第一层出队的同时带第二层入队,一层带一层,直到队列中所有节点都为NULL,就说明二叉树遍历结束了。
层序需要队列的代码,这里只列出层序的核心代码,关于队列部分的代码,可以在栈与队列章节中找到: 数据结构-栈和队列(一)-CSDN博客
层序代码:
//层序遍历
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->data);if (front->left)QueuePush(&q,front->left);if (front->right)QueuePush(&q,front->right);}printf("\n");DestoryQueue(&q);
}
注意,这里就是三层嵌套了,队列Queue中有头尾节点和队列大小size,头尾节点中有next指针和数据data,而数据data中存放的是二叉树的节点指针,所以我们在Queue.h中要把数据类型改成二叉树节点指针:
三层嵌套的关系如下图:
所以我们每次pop的都是队列的节点,而front是指向二叉树节点的指针,它没有被pop,所以可以找到二叉树节点的数据和它的左右孩子。
再补充一个内容,那就是二叉树的销毁:
二叉树销毁最好用后序,先销毁左右子树,最后销毁根节点,不建议使用前序,否则先销毁根节点,就找不到左右子树了,又要重新构建二叉树,比较麻烦。
//二叉树的销毁
void BTreeDestory(BTNode* root)
{if (root == NULL){return;}BTreeDestory(root->left);BTreeDestory(root->right);free(root);
}
7.二叉树遍历
题目链接:二叉树遍历_牛客题霸_牛客网
这道题其实包含了二叉树的创建和遍历, 相当于给一个字符串,要求把给字符串恢复成一个二叉树,然后输出它的中序遍历,那abc##de#g##f###,它是前序遍历的,我们就用前序把它恢复成二叉树:
创建二叉树时,也是先创建根节点,然后创建左子树、右子树。
注意:在传数组下标时,传地址,用指针接收。
代码如下:
#include <stdio.h>
#include<stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail\n");return NULL;}node->left = NULL;node->right = NULL;node->data = x;return node;
}
//创建二叉树
BTNode*CreateTree(char*a,int*pi)
{if(a[*pi]=='#'){(*pi)++;return NULL;}BTNode*root=BuyNode(a[*pi]);(*pi)++;root->left=CreateTree(a,pi);root->right=CreateTree(a,pi);return root;
}
//中序遍历
void InOrder(BTNode*root)
{if(root==NULL){return;}InOrder(root->left);printf("%c ",root->data);InOrder(root->right);}
int main()
{char a[100];scanf("%s",a);int i=0;BTNode*root=CreateTree(a,&i);InOrder(root);printf("\n");return 0;
}
8.判断二叉树是否是完全二叉树
思路:
这道题就要使用到层序遍历了,我们说层序遍历是一层带一层的入队和出队的过程,而完全二叉树的每一层都是按顺序排放的,让它的每一层数据入队,然后出队,直到遇见空,如果是完全二叉树,那此时最后一个数据出队后,后面应该全为NULL,而非完全二叉树就不全是NULL了,如下图:
所以当我们遇到空时就跳出循环,然后判断后面的是否全为空,一旦有非空,那就不是完全二叉树。
代码如下:
//判断二叉树是否是完全二叉树
bool BTreeComplete(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){DestoryQueue(&q);return false;}}DestoryQueue(&q);return true;
}
二叉树的一个特殊的性质:
对任何一棵二叉树, 如果度为0的叶结点个数为n0 , 度为2的分支结点个数为 n1,则有 n0=n1 +1
简单推导一下:
对二叉树增加一个度为1的节点就一定会减少一个度为0的节点,同时增加的这个节点度也为0,此时度为0和度为2的节点个数相当于没变:
而增加一个度为2的节点就一定会减少一个度为1的,同时会增加一个度为0的节点:
所以二叉树中度为0的节点总是比度为2的节点个数多一个。
下面我们来做道题:
在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2答案是:A
假设度为0的节点有n0个,度为1的有n1个,度为2的有n2个,根据上文学过的性质,可得:
n0 + n1 + n0-1=2n,而此时要整除(叶子节点一定是整数个),n1必然是1,所以可得n0 = 2n/2 = n
好了,到今天为止,我们二叉树就告一段落了,其实二叉树还有很多问题,这些留到后面C++的时候学习,
下节内容会接着讲解排序,敬请期待。。。
相关文章:

数据结构-二叉树力扣题
目录 1.相同的树 2.二叉树中查找值为x的节点 3.单值二叉树 4.对称二叉树 5.二叉树的前序遍历 6.另一颗树的子树 层序遍历: 7.二叉树遍历 8.判断二叉树是否是完全二叉树 一个特殊的性质: 1.相同的树 题目链接:力扣(LeetC…...

node 第十八天 中间件express-session实现会话密钥
express-session 文档 express-session 一个简单的express会话中间件 使用场景 在一个系统中, 需要维持一个临时的与登录态无关的会话密钥 比如登录系统后, 请求某一个接口, 接口的行为与登录态无关, 也就是说任何人对接口的访问…...

【机器学习基础】机器学习入门(1)
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!后面的内容会越来越有意思~ 💡专栏介绍: 本专栏的第一篇文章,当然要介绍一下了~来说一下这个专栏的开…...

赶快来!程序员接单必须知道的六大注意事项!!!
花花世界迷人眼,增加实力多搞钱!对于咱程序员来说,搞钱的最好办法就是网上接单了,相信也有不少小伙伴已经在尝试了吧!但是如何正确的搞钱呢?其中的注意事项你真的了解吗? 本期就和小编一起来看…...

【C++】日期类实现,与日期计算相关OJ题
文章目录 日期类的设计日期计算相关OJ题HJ73 计算日期到天数转换KY111 日期差值KY222 打印日期KY258 日期累加 在软件开发中,处理日期是一项常见的任务。为了方便地操作日期,我们可以使用C编程语言来创建一个简单的日期类。在本文中,我们将介…...

前端404页面的制作
1、背景 前端开发经常遇到输入路径不存在的问题,为此,把之前项目的404拿出来供大家参考。代码很简单,适合新手入手,效果如下: 2、代码引用的是element-plus框架 <template><div><el-result icon"…...

深兰科技轮腿家用AI机器人荣获“2023年度城市更新科创大奖”
近日,“2023金砖论坛第五季金立方城市更新科创大会”在上海举行,会上发布了《第12届金砖价值榜》,深兰科技研发出品的轮腿式家用AI机器人(兰宝),因其AI技术的创新性应用,荣获了“2023年度城市更新科创大奖”。 在10月2…...

669.修剪二叉树
原题链接:669.修剪二叉树 全代码: class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root nullptr ) return nullptr;if (root->val < low) {TreeNode* right trimBST(root->right, low, high); // 寻找符合区间[l…...

论文绘图-机器学习100张模型图
在现代学术研究和技术展示中,高质量的图表和模型结构图是至关重要的。这尤其在机器学习领域更为显著,一个领域以其复杂的算法和复杂的数据结构而闻名。机器学习是一种使用统计技术使计算机系统能够从数据中学习和改进其任务执行的方法,而有效…...

PHP项目学习笔记-萤火商城-增加一个模块(表涉及到的操作和文件)
背景 是在store的后台添加一个页面,显示的如满意度调查的页面 在router.config.js里面配置一个新的菜单 路径:yoshop2.0-store\src\config\router.config.js 代码如下,很简单,定义了这菜单点击的时候进入的页面,和下面…...

如何用Java设计自动售货机?
如何用Java设计自动售货机?是大多在高级Java开发人员面试中经常被问到的好问题之一。在典型的编码面试中,你会得到一个问题描述来开发一个售货机,在有限的时间内,通常2到3小时内,你需要在Java中编写设计文档、工作代码和单元测试。这种Java面试的一个关键优势是可以一次测试候…...

JAVA数据代码示例
首先,我们需要导入一些必要的Java库 java import java.net.URL; import java.net.HttpURLConnection; import java.io.BufferedReader; import java.io.InputStreamReader; 然后,我们可以创建一个URL对象,表示我们要爬取的网页的URL。 jav…...

github常用搜索指令
一、常用搜索指令 以下指令可分开用,也可组合使用 根据关键字搜索 in:name xx继上一步:指定开发语言 language:Java in:name XX language:Java继上一步,指定更新日期 pushed:>2022-06-06 in:name XX language:Java pushed:>2022-0…...

为什么esp8266刷入了固件,无法接受AT指令
我遇到的解决方法是:是串口调试助手出了问题。所以需要更换一个串口调试助手软件。 上面这个就是我换了的软件 在开发的时候,经常会遇到软件故障,导致正确的方法,但是没有效果,好比以前用盗版的8.7版本的Proteus模拟…...

Scala---字符串、集合
一、字符串 StringStringBuilder 可变string操作方法举例 比较:equals比较忽略大小写:equalsIgnoreCaseindexOf:如果字符串中有传入的assci码对应的值,返回下标 1./** 2.* String && StringBuilder 3.*/ 4.val str "abcd" 5.val s…...

Power Automate-当收到HTTP请求时触发流程
选择创建自动化云端流,点跳过 第一个操作搜索HTTP,点击当收到HTTP请求时 点击使用示例有效负载生成架构 写入JSON,点击完成 正文JSON架构就自动生成了,再点击左下角的显示高级选项 Method根据需求选择 可以选择JSON中的参数赋值给…...

学习c#的第十四天
目录 C# 接口(Interface) 接口的特点 定义接口 接口继承 接口和抽象类的区别 C# 命名空间(Namespace) using 关键字 定义命名空间 嵌套命名空间 C# 接口(Interface) 接口定义了所有类继承接口时应…...

6.jvm中对象创建流程与内存分配
目录 概述对象的创建流程对象的内存分配方式对象怎样才会进入老年代大对象直接进入老年代内存担保 jvc 相关指令查看jdk默认使用的gc查看当前jdk支持的有哪些gc查看指定进程当前正在使用的gc 结束 概述 相关文章在此总结如下: 文章地址jvm基本知识地址jvm类加载系…...

算法--搜索与图
这里写目录标题 主要内容DFS思想 BFS思想 DFS与BFS的比较一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 主要内容 DFS 思想 会优先向深处搜索 一旦到达最深处 那么会回溯 但是在回溯的过程中 会边回溯边观察是否有能继…...

ROS 文件系统
ROS文件系统级指的是在硬盘上ROS源代码的组织形式,ROS 的文件系统本质上都还是操作系统文件,可以使用Linux命令来操作这些文件,文件操作,包含增删改查与执行等操作,ROS文件系统的一些常用命令如下: 1.增加…...

车载通信与DDS标准解读系列(1):DDS-RPC
▎RPC & DDS-RPC RPC:Remote Procedure Call,远程过程调用。 远程过程调用是一种进程间通信,它允许计算机程序在另一个地址空间中执行子程序,就好像用别人的东西像用自己的一样,常用于分布式系统。 远程过程调用…...

通过构造树形结构介绍map的用法
构造TreeSelect树形结构: 当我们拿到的数据与我们要用的数据不一致时,就要改造成自己想要的数据结构。 后端拿到的数据结构: public class TPMGroup{public string DepName { get; set; }public List<staff> TPMList { get; set; }pu…...

代码随想录算法训练营Day 53 || 1143.最长公共子序列、1035.不相交的线、53. 最大子序和
1143.最长公共子序列 力扣题目链接 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何…...

Oracle JDBC数据库驱动程序介绍
Maven Central上所有Oracle JDBC数据库驱动程序 现在不仅可以在Maven Central上使用甲骨文数据库最新版本,而且还可以获得所有受支持的Oracle JDBC驱动程序发行版,包括19.3.0.0、18.3.0.0、12.2.0.1和11.2.0.4。从现在开始,Maven Central确实…...

scipy实现单因素方差分析
经典例题 某校高二年级共有四个班,采用四种不同的教学方法进行数学教学,为了比较这四种教学法的效果是否存在明显的差异,期末统考后,从这四个班中各抽取 5 名考生的成绩,如下所示。 班级 一班 二班 三班 四班 …...

深度学习实战59-NLP最核心的模型:transformer的搭建与训练过程详解,手把手搭建与跑通
大家好,我是微学AI,今天给大家介绍一下深度学习实战59-NLP最核心的模型:transformer的搭建与训练过程详解,手把手搭建与跑通。transformer是一种基于自注意力机制的深度学习模型,由Vaswani等人在2017年的论文《Attention is All You Need》中提出。它最初被设计用来处理序…...

一阶滤波器(一阶巴特沃斯滤波器)
连续传递函数G(s) 离散传递函数G(z) 转换为差分方程形式 一阶巴特沃斯滤波器Filter Designer参数设计:参考之前的博客Matlab的Filter Designer工具设计二阶低通滤波器 设计采样频率100Hz,截止频率20Hz。 注意:设计参数使用在离散系统中&…...

.net core中前端vue HTML5 History 刷新页面404问题
放到启动的应用程序的最后面 app.Run(async (context) > {context.Response.ContentType "text/html";await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html")); });https://blog.csdn.net/lee576/article/details/88355…...

【152.乘积最大子数组】
目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 class Solution { public:int maxProduct(vector<int>& nums) {int nnums.size();vector<int> f(n);vector<int> g(n);f[0]g[0]nums[0];int retnums[0];for(int i1;…...

如何开发OA系统场景的系统架构
1.开发OA系统场景的系统架构 针对开发OA系统的场景,以下是一个简单的系统架构示例,包括前端、后端和数据库三个基本部分: 前端: 使用React框架进行前端开发,构建用户界面和交互逻辑。前端模块包括日程管理模块、文档管…...