【经典算法】LeetCode101:对称二叉树(Java/C/Python3实现含注释说明,Easy)

对称二叉树

  • 题目描述
  • 思路及实现
    • 方式一:递归(推荐)
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式二:队列(迭代)
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
  • 总结
  • 相似题目

  • 标签:二叉树递归、对称性判断

题目描述

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:
在这里插入图片描述

输入:root = [1,2,2,3,4,4,3] 输出:true 示例 2:
在这里插入图片描述

输入:root = [1,2,2,null,3,null,3] 输出:false

提示:

树中节点数目在范围 [1, 1000] 内
-100 <= Node.val <= 100

进阶:你可以运用递归和迭代两种方法解决这个问题吗?

原题: LeetCode 101

思路及实现

方式一:递归(推荐)

思路

乍一看无从下手,但用递归其实很好解决。
根据题目的描述,镜像对称,就是左右两边相等,也就是左子树和右子树是相当的。
注意这句话,左子树和右子相等,也就是说要递归的比较左子树和右子树。
我们将根节点的左子树记做 left,右子树记做 right。比较 left 是否等于 right,不等的话直接返回就可以了。
如果相当,比较 left 的左节点和 right 的右节点,再比较 left 的右节点和 right 的左节点
比如看下面这两个子树(他们分别是根节点的左子树和右子树),能观察到这么一个规律:

左子树 2 的左孩子 == 右子树 2 的右孩子
左子树 2 的右孩子 == 右子树 2 的左孩子
2                 2
/  \              /   3    4          4     3
/  \    /  \      / \     /  8  7  6  5    5  6  7  8

根据上面信息可以总结出递归函数的两个终止条件:

  1. left 和 right 不等,或者 left 和 right 都为空
  2. 递归的比较 left,left 和 right.right,递归比较
    left,right 和 right.left

代码实现

Java版本
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;  // 如果根节点为null,即空树,视为对称二叉树,返回true
        }
        return isMirror(root.left, root.right);  // 调用isMirror方法判断左子树和右子树是否对称
    }

    private boolean isMirror(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;  // 如果左子树和右子树都为null,也视为对称,返回true
        }
        if (left == null || right == null) {
            return false;  // 如果左子树和右子树只有一个为null,视为不对称,返回false
        }
        return left.val == right.val && isMirror(left.left, right.right) && isMirror(left.right, right.left);
        // 如果左子树和右子树的值相等,且同时满足左子树的左子树和右子树的右子树对称,
        // 以及左子树的右子树和右子树的左子树对称,则视为对称,返回true;否则,返回false
    }
}

说明:
isSymmetric方法是该函数的入口,接收一个二叉树的根节点作为参数。首先判断根节点是否为null,如果是,即空树,视为对称二叉树,返回true。否则,调用isMirror 方法来判断左子树和右子树是否对称。

isMirror方法是递归判断左右子树是否对称的函数。首先判断左子树和右子树是否都为null,如果是,即均为空树,视为对称,返回true。然后判断左子树和右子树中只有一个为null的情况,即一个为空树一个不为空树,视为不对称,返回false。最后,判断左子树的值和右子树的值是否相等,并且同时递归判断左子树的左子树和右子树的右子树是否对称,以及递归判断左子树的右子树和右子树的左子树是否对称。只有全部满足才视为对称,返回true;否则,返回false。

C语言版本
#include <stdbool.h>

/*struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
*/

bool isMirror(struct TreeNode *left, struct TreeNode *right);

bool isSymmetric(struct TreeNode *root) {
    if (root == NULL) {
        return true;   // 如果根节点为NULL,即空树,视为对称二叉树,返回true
    }
    return isMirror(root->left, root->right);   // 调用isMirror函数判断左子树和右子树是否对称
}

bool isMirror(struct TreeNode *left, struct TreeNode *right) {
    if (left == NULL && right == NULL) {
        return true;    // 如果左子树和右子树都为NULL,也视为对称,返回true
    }
    if (left == NULL || right == NULL) {
        return false;   // 如果左子树和右子树只有一个为NULL,视为不对称,返回false
    }
    return (left->val == right->val) && isMirror(left->left, right->right) && isMirror(left->right, right->left);
    // 如果左子树和右子树的值相等,并且同时满足左子树的左子树和右子树的右子树对称,
    // 以及左子树的右子树和右子树的左子树对称,则视为对称,返回true;否则,返回false
}

Python3版本
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True  # 如果根节点为空,返回True,空树被认为是对称的

        return self.isMirror(root.left, root.right)

    def isMirror(self, left: TreeNode, right: TreeNode) -> bool:
        if left is None and right is None:
            return True  # 如果左子树和右子树都为空,认为是对称的

        if left is None or right is None:
            return False  # 如果左子树和右子树只有一个为空,不对称

        return left.val == right.val and self.isMirror(left.left, right.right) and self.isMirror(left.right, right.left)
        # 比较左子树的左子树和右子树的右子树,以及左子树的右子树和右子树的左子树,满足条件才认为是对称的


# 假设定义了TreeNode类,包含val、left和right属性
class TreeNode:
    def __init__(self, val: int = 0, left: TreeNode = None, right: TreeNode = None):
        self.val = val
        self.left = left
        self.right = right

说明:代码中使用了 TreeNode 类来定义树节点,包含 val、left 和 right 属性。其中 val 存储节点的值,left 和
right 存储左子树和右子树的引用。

复杂度分析

  • 时间复杂度:O(n),其中 n 表示树的节点数。
  • 空间复杂度:O(n),递归调用的栈空间最多为树的高度。

方式二:队列(迭代)

思路

回想下递归的实现:
当两个子树的根节点相等时,就比较:
左子树的 left 和 右子树的 right,这个比较是用递归实现的。
现在我们改用队列来实现,思路如下:
首先从队列中拿出两个节点(left 和 right)比较:
将 left 的 left 节点和 right 的 right 节点放入队列
将 left 的 right 节点和 right 的 left 节点放入队列

代码实现

Java版本
//leetcode submit region begin(Prohibit modification and deletion)
import java.util.LinkedList;

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return false;  // 根节点为空,不算对称
        }
        if (root.left == null && root.right == null) {
            return true;  // 左右子树都为空,算对称
        }
        
        LinkedList<TreeNode> queue = new LinkedList();  // 使用队列来保存待比较的节点
        queue.add(root.left);
        queue.add(root.right);
        
        while (queue.size() > 0) {
            TreeNode left = queue.removeFirst();
            TreeNode right = queue.removeFirst();
            
            // 只要两个节点都为空,继续循环;两者有一个为空,返回false
            if (left == null && right == null) {
                continue;
            }
            if (left == null || right == null) {
                return false;
            }
            
            // 判断两个节点的值是否相等
            if (left.val != right.val) {
                return false;
            }
            
            // 将左节点的左子节点和右节点的右子节点放入队列
            queue.add(left.left);
            queue.add(right.right);
            
            // 将左节点的右子节点和右节点的左子节点放入队列
            queue.add(left.right);
            queue.add(right.left);
        }
        
        return true;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

说明:
这段代码使用迭代方法判断二叉树是否对称。

在 isSymmetric 方法中,首先判断根节点是否为空,如果为空,返回 false,表示不对称。然后,如果根节点的左右子树都为空,返回 true,表示对称(只有一个元素的case)。

接下来,创建一个队列 queue,并将根节点的左子节点和右子节点加入队列。然后进入循环,每次从队列中取出两个节点进行比较。在比较过程中,只要两个节点均为空,继续循环;如果只有一个节点为空,返回
false,表示不对称。然后,比较两个节点的值是否相等,如果不相等,返回 false。

将左节点的左子节点和右节点的右子节点放入队列,用于后续比较。同时,将左节点的右子节点和右节点的左子节点放入队列,也用于后续比较。

当队列为空时,表示所有节点都已比较完毕,没有发现不对称的情况,返回 true,表示对称。

这段代码使用了迭代方法,利用队列存储待比较的节点,逐层按顺序比较,避免了递归方法的额外栈空间开销。

C语言版本
// leetcode submit region begin(Prohibit modification and deletion)
#include <stdbool.h>

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

bool isSymmetric(struct TreeNode* root) {
    if (root == NULL) {
        return false;  // 根节点为空,不算对称
    }
    
    struct TreeNode* queue[10000];  // 使用队列来保存待比较的节点
    int front = 0, rear = 0;
    
    queue[rear++] = root->left;
    queue[rear++] = root->right;
    
    while (rear != front) {
        struct TreeNode* left = queue[front++];
        struct TreeNode* right = queue[front++];
        
        // 只要两个节点都为空,继续循环;两者有一个为空,返回false
        if (left == NULL && right == NULL) {
            continue;
        }
        if (left == NULL || right == NULL) {
            return false;
        }
        
        // 判断两个节点的值是否相等
        if (left->val != right->val) {
            return false;
        }
        
        // 将左节点的左子节点和右节点的右子节点放入队列
        queue[rear++] = left->left;
        queue[rear++] = right->right;
        
        // 将左节点的右子节点和右节点的左子节点放入队列
        queue[rear++] = left->right;
        queue[rear++] = right->left;
    }
    
    return true;
}
//leetcode submit region end(Prohibit modification and deletion)

说明:
这段代码使用C语言实现了迭代方法来判断二叉树是否对称。

在 isSymmetric 函数中,首先判断根节点是否为空,如果为空,返回 false,表示不对称。

创建一个队列 queue,使用数组来保存待比较的节点。使用 front 和 rear 变量分别表示队首和队尾的索引。

将根节点的左子节点和右子节点依次加入队列 queue。

然后进入循环,每次从队列中取出两个节点进行比较。

在比较过程中,只要两个节点均为空,继续循环;如果只有一个节点为空,返回
false,表示不对称。然后,比较两个节点的值是否相等,如果不相等,返回 false。

将左节点的左子节点和右节点的右子节点放入队列,用于后续比较。同时,将左节点的右子节点和右节点的左子节点放入队列,也用于后续比较。

当队列为空时,表示所有节点都已比较完毕,没有发现不对称的情况,返回 true,表示对称。

这段代码使用了迭代方法,利用数组队列方式存储待比较的节点,逐个按序比较,避免了递归方法的额外栈空间开销。

Python3版本
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return False  # 根节点为空,不算对称
        
        queue = []
        queue.append(root.left)
        queue.append(root.right)
        
        while queue:
            left = queue.pop(0)
            right = queue.pop(0)
            
            if left is None and right is None:
                continue  # 只要两个节点都为空,继续循环
            
            if left is None or right is None:
                return False  # 两者有一个为空,返回False,不对称
            
            if left.val != right.val:
                return False  # 节点值不相等,不对称
            
            queue.append(left.left)  # 左节点的左子节点入队列
            queue.append(right.right)  # 右节点的右子节点入队列
            queue.append(left.right)  # 左节点的右子节点入队列
            queue.append(right.left)  # 右节点的左子节点入队列
        
        return True  # 队列为空,所有节点比较完毕,对称

说明:(基础说明可查看java或者c的实现)

  1. 在函数参数的类型注解中,使用了 TreeNode 类型来标注 root 参数的类型。

  2. 使用了 is 运算符来判断节点是否为 None。is 运算符比较的是对象的身份标识,用于判断对象是否是同一个对象。这里用它来判断节点是否为None。

  3. 使用了列表 queue 来实现队列的功能,通过 append() 方法将节点加入队列的尾部,通过 pop(0)
    方法来从队列的头部取出节点。Python的列表可以很方便地实现队列的功能。

复杂度分析

  • 时间复杂度:O(n),其中 n 表示树的节点数。在最坏情况下,需要遍历树的所有节点。
  • 空间复杂度:O(n),最坏情况下,队列中需要存储树的一层节点,所以空间复杂度与树的高度有关。在最坏情况下,树的高度为 n/2,因此空间复杂度为 O(n)。
    综合来看,这个算法的时间复杂度和空间复杂度都是 O(n),其中 n 表示树的节点数。算法的性能随着节点数的增加而线性增长。

总结

方法优点缺点时间复杂度空间复杂度
递归法- 直观易懂
- 代码相对简洁
- 可能导致函数调用栈溢出的风险
- 需要额外的空间来存储函数调用栈
O(n)O(n)
队列法- 不会导致函数调用栈溢出的风险
- 无需递归,代码较为直观
- 灵活的节点入队和出队顺序
- 需要手动维护队列数据结构和追踪节点的层次
- 需要额外的空间来存储队列和节点的信息
O(n)O(m)

相似题目

题目描述难度
LeetCode 100判断两个二叉树是否相同Easy
LeetCode 226反转二叉树Easy
LeetCode 104计算二叉树的最大深度Easy
LeetCode 110判断二叉树是否平衡Easy
LeetCode 222统计完全二叉树的节点个数Medium
LeetCode 124计算二叉树中的最大路径和Hard
LeetCode 199返回二叉树从右侧看的节点值列表Medium
LeetCode 116填充二叉树的每个节点的下一个右侧节点指针Medium
LeetCode 112判断二叉树是否存在从根节点到叶子节点的路径和等于给定目标值Easy
LeetCode 257返回二叉树所有从根节点到叶子节点的路径Easy

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【XR806开发板试用】2、UDP控制的呼吸灯

【XR806开发板试用】1、UDP通信测试 上篇文章测试了XR806的UDP通信. 控制PWM控制相关的函数在device/xradio/xr806/adapter/hals/iot_hardware/wifiiot_lite文件夹下的iot_pwm.c . ├── BUILD.gn ├── iot_flash.c ├── iot_gpio.c ├── iot_i2c.c ├── iot_pwm.c ├…

车载以太网AVB交换机 TSN交换机 时间敏感网络 11口 千兆 SW2000TSN

目录 一、TSN时间敏感交换机概述 二、产品介绍 SW2000M/H TSN 1、产品框架 2、产品特点与参数 产品特点 产品参数 3、配置与使用 4、常用连接方式 4.1 双通道作为监控和数据采集器&#xff0c;采集两个设备间的通信数据&#xff08;Bypass功能&#xff09; 4.2 试验搭…

[中级]软考_软件设计_计算机组成与体系结构_05_CISC与RISC

CISC与RISC CISC&RISC的基本概念对比的维度往年真题 CISC&RISC的基本概念 对比的维度 CISC:指令多&#xff0c;使用频率差别大&#xff0c;变长格式&#xff0c;多种寻址方式RISC:指令少&#xff0c;使用频率接近&#xff0c;定长格式&#xff0c;多为寄存器寻址&#…

不锈钢潜水排污泵80WQP40-15-3S

一、产品概述&#xff1a;不锈钢潜水排污泵80WQP40-15-3S是一款专门设计用于污水排放的高性能潜水电泵。该泵采用了高级不锈钢材质&#xff0c;确保了泵体的耐腐蚀性和耐久性&#xff0c;非常适合在恶劣的工作环境中运行。其型号中的“80”指的是泵的出口为80mm&#xff0c;“W…

【竞技宝jjb.lol】LOL:LPL春季常规赛荣誉评选出炉!

北京时间2024年4月3日,英雄联盟LPL2024春季季后赛正在如火如荼的进行之中,常规赛阶段的荣誉评选结果在近日出炉,除三个最佳阵容之外,其中BLG战队的中单选手knight斩获春季赛常规赛MVP,而FPX战队的打野选手milkway则拿到春季赛常规赛的最佳新秀。 春季常规赛最有价值选手:BLG.kn…

Redis 全景图(1)--- 关于 Redis 的6大模块

这是我第一次尝试以长文的形式写一篇 Redis 的总结文章。这篇文章我想写很久了&#xff0c;只是一直碍于我对 Redis 的掌握没有那么的好&#xff0c;因此迟迟未动笔。这几天&#xff0c;我一直在看各种不同类型的 Redis 文章&#xff0c;通过阅读这些文章&#xff0c;引发了我对…

nestjs 全栈进阶--控制器和参数获取

视频教程 06_nest控制器和参数获取1_哔哩哔哩_bilibili nest new argument -p pnpm nest g resource person pnpm start:dev 测试下&#xff1a;http://localhost:3000/person/1 在浏览器中看到图中的内容就是成功了 1. 路由 在Nest.js中&#xff0c;路由是由Controller装…

CTF比赛中JWT漏洞的利用

前言 在最近的ctf比赛中&#xff0c;经常可以碰到一些jwt相关的题目&#xff0c;然后感觉思路挺有意思&#xff0c;拿出来分享一下&#xff0c;后边也总结一下ctf比较常见的集jwt相关题目解题思路 算法混淆漏洞 腾龙杯 web[这又是一个登录页面] 使用zkaq/zkaq登录之后&#…

在编程中使用中文到底该不该??

看到知乎上有个热门问题&#xff0c;为什么很多人反对中文在编程中的使用&#xff1f; 这个问题有几百万的浏览热度&#xff0c;其中排名第一的回答非常简洁&#xff0c;我深以为然&#xff1a; 在国内做开发&#xff0c;用中文写注释、写文档&#xff0c;是非常好的习惯&…

【C+ +初阶】前言篇章---C+ +的广袤

目录 1.C语言到C的过渡 2.C的发展历程 2.1C语言的诞生 2.2 c的历史版本 3.c 的地位 4. c的应用场景 4.1. 操作系统以及大型系统软件开发 所有操作系统几乎都是C/C写的 4.2. 服务器端开发 后台开发&#xff1a; 4.3. 游戏开发 4.4. 嵌入式 4.5. 数字图像处理 4.6. 人工智能 4.7.…

pygame--坦克大战(二)

加载敌方坦克 敌方坦克的方向是随机的&#xff0c;使用随机数生成。 初始化敌方坦克。 class EnemyTank(Tank):def __init__(self,left,top,speed):self.images {U: pygame.image.load(img/enemy1U.gif),D: pygame.image.load(img/enemy1D.gif),L: pygame.image.load(img/e…

IP地址获取不到的原因是什么?

在数字化时代的今天&#xff0c;互联网已成为我们日常生活和工作中不可或缺的一部分。而IP地址&#xff0c;作为互联网通信的基础&#xff0c;其重要性不言而喻。然而&#xff0c;有时我们可能会遇到IP地址获取不到的问题&#xff0c;这会给我们的网络使用带来诸多不便。那么&a…

基于单片机的光伏电量检测系统的设计

**单片机设计介绍&#xff0c;基于单片机的光伏电量检测系统的设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的光伏电量检测系统的设计概要主要围绕实现光伏电量的实时监测与精准测量展开。以下是该设计的主要内…

flutter获取手机中的系统路径信息

https://www.bilibili.com/video/BV1wE421g7sw获取系统中的路径 获取系统中的路径&#xff0c;并在这个路径中创建一个文本文件【str.txt】 然后进行写入【str.txt】 再读取这个文件【str.txt】 手机没有开通root权限无法看到写入到【应用程序文档目录】路径中的文件 用来…

大创项目推荐 深度学习 python opencv 火焰检测识别 火灾检测

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…

Node.js介绍

Node.js 是一个开源和跨平台的 JavaScript 运行时环境。它是几乎任何类型的项目的流行工具&#xff01;

剑指Offer题目笔记25(使用回溯法解决其他类型问题)

面试题85&#xff1a; 问题&#xff1a; ​ 输入一个正整数n&#xff0c;输出所有包含n个左括号和n个右括号的组合&#xff0c;要求每个组合的左括号和右括号匹配。 解决方案&#xff1a; ​ 使用回溯法。因为要生成n个左括号和n个右括号&#xff0c;故需要走2n步&#xff0…

LabVIEW专栏三、探针和断点

探针和断点是LabVIEW调试的常用手段&#xff0c;该节以上一节的"测试耗时"为例 探针可以打在有线条的任何地方&#xff0c;打上后&#xff0c;经过这条线的所有最后一次的数值都会显示在探针窗口。断点可以打在程序框图的所有G代码对象&#xff0c;包括结构&#xf…

浅谈通信校验码及 CRC 校验

一、信息论中的 CRC 我上大学的时候,有一门课程叫做信息论,我就是从这个课程中学到的 CRC 校验这个词的,没错,当时学完整个课程后,CRC 对我来说依然只是一个单薄的缩写词语,全称我都不知道是啥。 CRC 全称是循环冗余校验(Cyclic Redundancy Check)。 说到信息论中的…

28. BI - 图论工具和算法, 社区发现以及最短路径

本文为 「茶桁的 AI 秘籍 - BI 篇 第 28 篇」 Hi, 我是茶桁. 咱们已经讲了好几节课的 PageRank, 也了解了 PageRank 的原理就是基于图论. 之前我有给讲过, 在「数学篇」中我们有用四个章节来详细的讲解图论的相关知识。其中包括&#xff1a; 23. 图论 - 图的由来和构成24. 图…