对称二叉树

力扣题目链接(opens new window)

给定一个二叉树,检查它是否是镜像对称的。

思路

本题中,不能单纯去比较左右子节点的是否对称(都有值且不为空)

因为如果按上面那样做的话,到子节点后就肯定是不对称的(对于左半边而言),但整体上看可能还是对称的,仍然满足题意,由此就会出现错误

因此,我们需要比较的是当前左右节点下的外侧内侧的节点是否对称,如图所示:

上图中,显然整课二叉树是对称的

我们在判断的时候只需将左2节点与右2节点的子节点按内侧与外侧区分,再进行比较即可

代码

递归法

按照递归三步走来:

1、确定递归函数的参数和返回类型

2、确定递归的终止条件

3、处理下一层的递归逻辑

class Solution {
public:
//确定递归函数的参数和返回值
bool cmp(TreeNode* left, TreeNode* right){
//确定终止条件
//四种情况
//空节点情况
//左节点为空,右节点不为空
if(left == NULL && right != NULL) return false;
//左节点不为空,右节点为空
else if(left != NULL && right == NULL) return false;
//左节点为空,右节点为空
else if(left == NULL && right == NULL) return true; //非空但值不相等情况
//左右节点均不为空但值不相等
else if(left->val != right->val)return false; //以下是左右节点均不为空且值相等的情况
//启用递归去判断他们的子节点(下一层)是否满足对称
//处理单层逻辑
bool outside = cmp(left->left, right->right);//外侧
bool inside = cmp(left->right, right->left);//内侧
bool res = outside && inside;
return res;//记得返回最终结果
}
bool isSymmetric(TreeNode* root) {
//判断根节点是否为空
if(root == NULL) return 0;
return cmp(root->left, root->right);
}
};
迭代法

定义两个节点,同时放入 队列 中,然后同时取出判断是否相等,直到遍历结束

class Solution {
public:
bool isSymmetric(TreeNode* root) {
//创建队列
queue<TreeNode*> que;
//判断根节点是否为空,为空直接对称
if (root == NULL) return true;
//获取左右节点(相对于root来说的)
//加入队列
que.push(root->left);
que.push(root->right); //队列不为空则遍历
while(!que.empty()){
//取出队列中的两个节点
TreeNode* left = que.front();
que.pop();
TreeNode* right = que.front();
que.pop();
// //左节点为空,右节点有值//左节点有值,右节点为空//左右节点有值但不同
// if(left == NULL && right != NULL || left != NULL && right == NULL || left->val != right->val){
// return false;
// }else if(left == NULL && right == NULL){
// continue;
// }
//左右节点均为空
if(left == NULL && right == NULL){
continue;
} //队列中如果没有同时放入两个节点就直接代表不对称了,即有一个为空就可以下判断
//其实还是对应之前的三种情况,只是在队列中表现方式不同
if((right == NULL || left == NULL|| (left->val != right->val))){
return false;
}
//排除上面的情况后就剩下不为空且对称的情况 //继续加入当前左右节点的子节点,依旧遵循内外侧原则
//外侧
que.push(left->left);
que.push(right->right);
//内侧
que.push(left->right);
que.push(right->left);
}
//完成上述遍历没返回false就是对称的
return true;
}
};
注意点

1、因为已经使用了队列,每次我们都需要放入两个节点,如果某次发现取的时候只有一个,那直接就可以判定当前情况为不对称‘

相同的树

https://leetcode.cn/problems/same-tree/

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:p = [1,2,3], q = [1,2,3]

输出:true

示例 2:

输入:p = [1,2], q = [1,null,2]

输出:false

示例 3:

输入:p = [1,2,1], q = [1,1,2]

输出:false

思路

与对称二叉树的解法类似

只不过这里递归函数中,我们输入的分别是两个树的root

在处理单层逻辑时,要遵循以下原则:

二叉树A的左侧节点与二叉树B的左侧节点比较;

二叉树A的右侧节点与二叉树B的右侧节点比较;

上述结果完全相同才能证明两树相同

代码

使用递归法的思路

class Solution {
public:
//确定递归函数参数与返回值
bool cmp(TreeNode* p, TreeNode* q){
//确定终止条件(当前遍历节点为空)
if(p == NULL && q == NULL) return true;
else if(p == NULL || q == NULL)return false;
else if(p->val != q->val)return false;
//处理单层逻辑
bool leftside = cmp(p->left, q->left);
bool rightside = cmp(p->right, q->right);
bool res = leftside && rightside;
return res;
}
bool isSameTree(TreeNode* p, TreeNode* q) {
return cmp(q,p);
}
};

另一颗子树

https://leetcode.cn/problems/subtree-of-another-tree/

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入:root = [3,4,5,1,2], subRoot = [4,1,2]

输出:true

示例 2:

输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]

输出:false

提示:

root 树上的节点数量范围是 [1, 2000]

subRoot 树上的节点数量范围是 [1, 1000]

-104 <= root.val <= 104

-104 <= subRoot.val <= 104

思路

先读懂题目,题目要求我们在root中寻找一个与subRoot相等的子树,如果存在该子树返回true,否则返回false

那么会有以下几种情况:

1、root与subRoot直接就是相等的

  • 两者都为NULL
  • 两者满足相等条件

2、subRoot是root的左子树

3、subRoot是root的右子树

分别针对上述情况进行处理即可

两颗二叉树怎么才算相等?

两颗二叉树相等的定义是它们的结构相同对应节点的值也相同

具体来说,以下条件满足时,两颗二叉树才算相等:

  1. 两棵树的根节点的值相等。
  2. 递归比较两棵树的左子树和右子树是否相等。如果都相等,则这两棵树相等;如果至少有一棵子树不相等,则这两棵树不相等。

需要注意的是,如果一棵树的节点为空,而另一棵树对应节点不为空,则这两棵树不相等。

代码

主函数

一般来说,如果涉及递归操作,都应该先讲递归函数

但是这里有点特殊,我想先说一下主函数(不然会不清楚递归函数要达到的目的)

class Solution {
public:
//先得有一个用于判断相等数的函数
//确定递归函数参数与返回值
bool traversal(TreeNode* p, TreeNode* q){ } bool isSubtree(TreeNode* root, TreeNode* subRoot) {
//root不能为空
if(root == NULL) return false; //root与subRoot相同,true
// if(traversal(root, subRoot))return true;
if(root->val == subRoot->val && traversal(root, subRoot)) return true; //分别判断root的左边和右边有无与subRoot相同的,满足一边即可
// return isSameTree(root->left, subRoot) || isSameTree(root->right, subRoot);
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
};

在主函数isSubtree中,我们要判断的root中是否存在subRoot

首先得确定root的根节点不为空

然后判断当前节点(根节点)的值是否与subRoot当前节点的值相等【两棵树的根节点的值相等

除此之外,还要判断root中是否存在与subRoot相等的子树【结构上相等】

判断结构的时候就需要使用递归函数遍历所有节点了,待会讲递归函数

如果在结构和数值上都相等,那么可以返回true

否则需要在再继续去root的左右子树中找与subRoot相等的子树

具体到操作上就是:递归调用主函数

是的,就是因为这个迷惑的操作,所以需要先说主函数逻辑。。。

在递归查找root左右子树时,只要有一边找到与subRoot相等的子树,主函数就可以返回true

递归函数

根据上述分析,整体代码中其实使用了两处递归

主函数递归负责在root中递归查找左右子树

而traversal则负责在主函数递归中,判断当前root的子树是否与subRoot相等

class Solution {
public:
//先得有一个用于判断相等数的函数
//确定递归函数参数与返回值
bool traversal(TreeNode* p, TreeNode* q){
//确定终止条件(当前遍历节点为空)
if(p == NULL && q == NULL) return true;//当前root子树和subRoot均为空的情况,相等
else if(p == NULL || q == NULL) return false;//当前root子树和subRoot一方为空的情况,不相等
else if(p->val != q->val) return false;//当前root子树和subRoot值不相等,不相等 //处理单层逻辑
//递归查找当前root子树的左子树
bool leftside = traversal(p->left, q->left);
//递归查找当前root子树的右子树
bool rightside = traversal(p->right, q->right);
//综合左右子树情况,均返回true则表示结构上相等,判定当前root子树和subRoot相等
bool res = leftside && rightside;
return res;
} bool isSubtree(TreeNode* root, TreeNode* subRoot) { }
};
完整代码
class Solution {
public:
//先得有一个用于判断相等数的函数
//确定递归函数参数与返回值
bool traversal(TreeNode* p, TreeNode* q){
//确定终止条件(当前遍历节点为空)
if(p == NULL && q == NULL) return true;//当前root子树和subRoot均为空的情况,相等
else if(p == NULL || q == NULL) return false;//当前root子树和subRoot一方为空的情况,不相等
else if(p->val != q->val) return false;//当前root子树和subRoot值不相等,不相等 //处理单层逻辑
//递归查找当前root子树的左子树
bool leftside = traversal(p->left, q->left);
//递归查找当前root子树的右子树
bool rightside = traversal(p->right, q->right);
//综合左右子树情况,均返回true则表示结构上相等,判定当前root子树和subRoot相等
bool res = leftside && rightside;
return res;
} bool isSubtree(TreeNode* root, TreeNode* subRoot) {
//root不能为空
if(root == NULL) return false; //root与subRoot相同,true
// if(traversal(root, subRoot))return true;
if(root->val == subRoot->val && traversal(root, subRoot)) return true; //分别判断root的左边和右边有无与subRoot相同的,满足一边即可
// return isSameTree(root->left, subRoot) || isSameTree(root->right, subRoot);
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
};

树的子结构

https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:

给定的树 A:

 3
/ \

4 5

/

1 2

给定的树 B:

4

/

1

返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]

输出:false

示例 2:

输入:A = [3,4,5,1,2], B = [4,1]

输出:true

限制:

0 <= 节点个数 <= 10000

思路

与上一题思路一致

代码

代码方面,递归函数的停止条件需要做一下变化

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
bool traversal(TreeNode* A, TreeNode* B){
//确定停止条件
if(B == NULL) return true;//当B遍历到了叶子节点,意味着B树是A树的子结构
//A、B同时为空表示没找到相同子结构,A先为空则表示B不可能为子结构,均返回false
if(A == NULL && B == NULL || A == NULL) return false;
if(A->val != B->val) return false;
// if(A == NULL || A->val != B->val) return false;//也可以这样判断 //确定单层处理逻辑
bool leftside = traversal(A->left, B->left);
bool rightside = traversal(A->right, B->right);
bool res = leftside && rightside;
return res;
}
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A == NULL || B == NULL) return false;
if(A->val == B->val && traversal(A, B)) return true;//确保结构一致 return isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
};

【LeetCode二叉树#04】判断对称二叉树、相同的树、另一棵子树、树的子结构(二叉树相等判断)的更多相关文章

  1. C语言实现二叉树-04版

    二叉树,通常应当是研究其他一些复杂的数据结构的基础.因此,通常我们应该精通它,而不是了解:当然,可能并不是每个人都认同这种观点,甚至有些人认为理解数据结构就行了!根本没有必要去研究如何实现,因为大多数 ...

  2. 【LeetCode】Symmetric Tree(对称二叉树)

    这道题是LeetCode里的第101道题.是我在学数据结构——二叉树的时候碰见的题. 题目如下: 给定一个二叉树,检查它是否是镜像对称的. 例如,二叉树 [1,2,2,3,4,4,3] 是对称的. 1 ...

  3. 判断对称二叉树 python代码

    对称二叉树的含义非常容易理解,左右子树关于根节点对称,具体来讲,对于一颗对称二叉树的每一颗子树,以穿过根节点的直线为对称轴,左边子树的左节点=右边子树的右节点,左边子树的右节点=左边子树的左节点.所以 ...

  4. Leetcode:面试题28. 对称的二叉树

    Leetcode:面试题28. 对称的二叉树 Leetcode:面试题28. 对称的二叉树 Talk is cheap . Show me the code . /** * Definition fo ...

  5. 剑指offer17:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

    1 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 2 思路和方法 (1)先在A中找和B的根节点相同的结点 (2)找到之后遍历对应位置的其他结点, ...

  6. [Leetcode] Construct binary tree from preorder and inorder travesal 利用前序和中续遍历构造二叉树

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:  You may assume tha ...

  7. 剑指offer-重建二叉树04

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  8. 数据结构(3) 第三天 栈的应用:就近匹配/中缀表达式转后缀表达式 、树/二叉树的概念、二叉树的递归与非递归遍历(DLR LDR LRD)、递归求叶子节点数目/二叉树高度/二叉树拷贝和释放

    01 上节课回顾 受限的线性表 栈和队列的链式存储其实就是链表 但是不能任意操作 所以叫受限的线性表 02 栈的应用_就近匹配 案例1就近匹配: #include <stdio.h> in ...

  9. 数据结构(一)二叉树 & avl树 & 红黑树 & B-树 & B+树 & B*树 & R树

    参考文档: avl树:http://lib.csdn.net/article/datastructure/9204 avl树:http://blog.csdn.net/javazejian/artic ...

  10. 剑指offer-面试题26-树的子结构-二叉树

    /* 题目: 输入两棵二叉树A和B,判断B是不是A的子树. */ /* 思路: 1.注意浮点数大小的判断. 2.判断树A的某个节点是否和树B的根节点是否相同, 若相同,则判断以A该节点为根节点是否包含 ...

随机推荐

  1. [转帖]查看堆内对象的工具:jmap

    文章目录 用途 命令格式 示例 一,no option 二,heap 三,histo[:live] 四,clstats 五,finalizerinfo 六,dump:<dump-options& ...

  2. [转帖]LTP使用和分析

    一.安装及编译流程 1.下载LTP LTP 项目目前位于 GitHub,项目地址:https://github.com/linux-test-project/ltp . 获取最新版可以执行以下命令: ...

  3. [转帖]Linux 中unzip解压时中文乱码的解决办法

    https://www.yii666.com/blog/163883.html Linux 中unzip解压时中文乱码的解决办法 当我们在linux中解压一个含有中文名字的压缩包如"资料.z ...

  4. CentOS7升级Glibc到超过2.17版本无法启动的解决办法

    CentOS7升级Glibc到超过2.17版本无法启动的解决办法 背景 今天有同事告知服务器宕机无法启动. 提示信息为: [sda] Assuming drive cache: write throu ...

  5. OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法

    OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法 公司有一台性能比较好的power机器. 同事要求安装rabbitmq 今天尝试进行了一下处理 公司里面有网络有相应的源 性能 ...

  6. 【VMware vSAN】使用命令行从vSAN集群中移除ESXi主机并加入到新的vSAN集群。

    说明 本文只是陈述了一种方法,不必评判谁对谁错谁好谁坏,选择适合自己的即可. 环境 站点名称 vCenter版本 vSAN集群 集群主机 主机版本 磁盘组 vcsa67.lab.com vCenter ...

  7. TienChin 渠道管理-表创建

    在若依当中,有个槽点,就是数据库当中的删除标识状态一般 0 是 false,1 是 true,在若依当中反而 0 是 true,1 是 false. 渠道表设计,我这里就直接贴成品的创建表 SQL: ...

  8. 微信小程序-页面生命周期方法

    在经过上一篇文章的介绍之后,我们知道了大体的生命周期在什么时候执行,这次主要是以代码的形式来展示一下具体的阶段执行什么生命周期方法. 首先我们编写一个代码可以从首页跳转到日志页面: <!--in ...

  9. Python 运用zabbix开发简易巡检工具

    利用SSH或者Zabbix监控,配合Django开发框架,改造出属于自己的监控平台,实现包括主机图形,自动发现,计划任务,批量cmd执行,服务监控,日志监控等功能,由于公司机器混乱,基本上市面上的所有 ...

  10. jackson、objectMapper 、JsonAlias、JsonProperty、json。序列化和反序列化研究。

    总结: @JsonAlias:序列化出来是一定是真名,反序列化时可以传真名(驼峰)Json也可以传别名(下划线)Json @JsonProperty:序列化出来一定是别名,反序列化时也只能传别名Jso ...