【刷爆力扣之101.对称二叉树-100.相同的树】

101.对称二叉树

1.递归法

递归三部曲

  1. 确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

代码如下:

private boolean doIsSymmetric(TreeNode left, TreeNode right) {
  1. 确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

代码如下:

// 若同时为 null
if (left == null && right == null) {
    return true;
}
// 若有一个为 null (有上一轮筛选,另一个肯定不为 null)
if (left == null || right == null) {
    return false;
}
// 若两节点值不相等,返回false
if (!Objects.equals(left.val, right.val)) {
    return false;
}
  1. 确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。

代码如下:

boolean a = doIsSymmetric(left.left, right.right);
boolean b = doIsSymmetric(left.right, right.left);
return a && b;

如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)。

最后递归的Java整体代码如下:

public boolean isSymmetric(TreeNode root) {
    if (root == null) {
        return false;
    }
    return doIsSymmetric(root, root);
}

private boolean doIsSymmetric(TreeNode left, TreeNode right) {
    // 若同时为 null
    if (left == null && right == null) {
        return true;
    }
    // 若有一个为 null (有上一轮筛选,另一个肯定不为 null)
    if (left == null || right == null) {
        return false;
    }
    // 若两节点值不相等,返回false
    if (!Objects.equals(left.val, right.val)) {
        return false;
    }
    boolean a = doIsSymmetric(left.left, right.right);
    boolean b = doIsSymmetric(left.right, right.left);
    return a && b;
}

2.迭代法

这道题目我们也可以使用迭代法,但要注意,这里的迭代法可不是前中后序的迭代写法,因为本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。

这里我们可以使用队列来比较两个树(根节点的左右子树)是否相互翻转,(注意这不是层序遍历

使用队列

如下的条件判断和递归的逻辑是一样的。

代码如下:

/**
     * 迭代法
     * 使用普通队列
     */
public boolean isSymmetric3(TreeNode root) {
    Queue<TreeNode> deque = new LinkedList<>();
    deque.offer(root.left);
    deque.offer(root.right);
    while (!deque.isEmpty()) {
        TreeNode leftNode = deque.poll();
        TreeNode rightNode = deque.poll();
        if (leftNode == null && rightNode == null) {
            continue;
        }
        if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
            return false;
        }
        // 这里顺序与使用Deque不同
        deque.offer(leftNode.left);
        deque.offer(rightNode.right);
        deque.offer(leftNode.right);
        deque.offer(rightNode.left);
    }
    return true;
}

使用栈

细心的话,其实可以发现,这个迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较,那么其实使用栈也是可以的。

/**
     * 迭代法
     * 使用双端队列,相当于两个栈
     */
public boolean isSymmetric2(TreeNode root) {
    Deque<TreeNode> deque = new LinkedList<>();
    deque.offerFirst(root.left);
    deque.offerLast(root.right);
    while (!deque.isEmpty()) {
        TreeNode leftNode = deque.pollFirst();
        TreeNode rightNode = deque.pollLast();
        if (leftNode == null && rightNode == null) {
            continue;
        }
        if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
            return false;
        }
        deque.offerFirst(leftNode.left);
        deque.offerFirst(leftNode.right);
        deque.offerLast(rightNode.right);
        deque.offerLast(rightNode.left);
    }
    return true;
}

100.相同的树 

这道题和上面的101对称二叉树本质上是同一道题,都是判断两颗二叉树的各个位置的节点是否相同,对称二叉树主要看同一颗树的左右两颗子树是否是相互对称的,因此需要判断左子树的内侧节点以及外侧节点与右子树的内侧节点与外侧节点是否相同,而本题主要判断两颗树相同位置的节点是否相同,本质上都是一样的,思路上也有很多相同之处。

本地依然可以使用递归和迭代两种方式解题:

递归:

依然是熟悉的递归三部曲:

  1. 判断递归的参数和返回值:
    由于本题需要判断两颗树是否相同,因此需要在递归中遍历到两颗树的所有节点,故递归的参数需要两颗树的根节点,返回值就是bool类型
public boolean isSameTree(TreeNode p, TreeNode q) {
  1. 判断递归的终止条件:

递归的终止条件有三种:

    • 遍历到的两颗树的节点都为null,返回true。
    • 遍历到的两颗树的节点只有一个为null,返回false。
    • 遍历到的两颗树的节点都不为null,但是两颗树的节点的值不同,返回false。
if (p == null && q == null) {
    return true;
}
// 通过上面的判断,这个判断如果为true一定是两个节点一个为null一个不为null,返回false
if (p == null || q == null) {
    return false;
}
if (p.val != q.val) {
    return false;
}
  1. 判断单次递归的逻辑:

单次递归的逻辑只有在两个节点都不为null,并且两个节点的值相同的情况下才会执行,因此单次递归的逻辑就是继续比较两个节点的子节点是否相同,这里也是和101.对称二叉树 解法不同的地方,因为101题强调两颗树的对称,因此在比较子节点时,是将左子树的左节点和右子树的右节点比较,即比较子树的外侧,以及左子树的右节点和右子树的左节点,即比较子树的内侧,而本题是比较两颗树是否相同,因此应该比较相同位置的节点是否相同,因此应该比较两个节点的左节点是否相同以及两个节点的右节点是否相同。

// 判断两个节点的左子树是否是相同的
boolean a = isSameTree(p.left, q.left);
// 判断两个节点的右子树是否是相同的
boolean b = isSameTree(p.right, q.right);
return a && b;

完整的递归代码如下:

// 递归
public boolean isSameTree(TreeNode p, TreeNode q) {
    if (p == null && q == null) {
        return true;
    }
    // 通过上面的判断,这个判断如果为true一定是两个节点一个为null一个不为null,返回false
    if (p == null || q == null) {
        return false;
    }
    if (p.val != q.val) {
        return false;
    }
    // 判断左子树是否是相同的
    boolean a = isSameTree(p.left, q.left);
    // 判断右子树是否是相同的
    boolean b = isSameTree(p.right, q.right);
    return a && b;
}

迭代:

迭代法用到了与101.对称二叉树相同的思想,每次将下次需要比较的两个节点依次加入到一个队列数据结构,如果队列不为空,则拿出队列中的两个元素进行判断和比较,具体的判断逻辑和上述递归方法中的第二个步骤相同,因此直接给出代码实现:

注意:

入队的顺序必须是两个节点比较的顺序,并且两个需要比较的节点必须相邻入队。

//迭代
    public boolean isSameTree2(TreeNode p, TreeNode q) {
        // 队列
        Queue<TreeNode> queue = new LinkedList<>();
        // 需要比较的元素入队,第一次入队也可以叫为初始化队列
        queue.offer(p);
        queue.offer(q);
        while (!queue.isEmpty()) {
            // 弹出需要比较的两个节点进行比较
            TreeNode pNode = queue.poll();
            TreeNode qNode = queue.poll();
            if (pNode == null && qNode == null) {
                continue; // 都为null,跳出本次循环,比较队列中其他节点
            }
            if (pNode == null || qNode == null || qNode.val != pNode.val) {
                return false;
            }
            // 将需要比较的两个节点依次放入队列
            queue.offer(pNode.left);
            queue.offer(qNode.left);
            queue.offer(pNode.right);
            queue.offer(qNode.right);
        }
        return true;
    }

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

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

相关文章

一键复制:基于vue实现的tab切换效果

需求&#xff1a;顶部栏有切换功能&#xff0c;内容区域随顶部切换而变化 目录 实现效果实现代码使用示例在线预览 实现效果 如下 实现代码 组件代码 MoTab.vue <template><div class"mo-tab"><divv-for"item in options"class"m…

地图位置的二维码怎么做?在线制作地图二维码的方法

怎么定位一个位置做成二维码呢&#xff1f;随着互联网的不断发展&#xff0c;现在通过扫描二维码来获取导航位置的方式有很多的场景都在应用。这种方式的好处在于其他人都可以通过这个二维码来获取位置&#xff0c;有利于分享。 导航地图二维码可以在电脑的二维码生成器上快速…

教练预约管理小程序开发源码现成案例(小程序、APP、H5圆源码搭建)

随着人们对身体健康越来越重视&#xff0c;对强身健体、健康个性化生活的需求日益增加&#xff0c;健身已成为时尚生活的标志。 然而&#xff0c;没有时间去健身房却成了很多上班族的痛点。健身房作为一项既能缓解工作压力又能缓解学业压力的运动&#xff0c;正好满足了当代人…

C++:map和set类

关联式容器 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面 存储的是元素本身。那什么是关…

《intel开发手册卷1》学习笔记2

1、栈 堆栈&#xff08;见图 6-1&#xff09;是一个连续的内存位置数组。它包含在一个段中&#xff0c;并由 SS 寄存器中的段选择器标识。使用平面内存模型时&#xff0c;堆栈可以位于程序线性地址空间中的任何位置。堆栈最长可达 4 GB&#xff0c;这是段的最大大小。 使用 P…

js原型链与继承笔记

前置阅读&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain js中的“类”是一个函数。function test() {}中&#xff0c;test是由Function生成的。prototype与__proto__的区别&#xff1a; 前者是js函数&#xff08;C…

拔牙护理:让孩子轻松迎接新牙成长的注意事项

引言&#xff1a; 拔牙是儿童成长过程中常见的一环&#xff0c;正确的护理和注意事项对孩子的口腔健康至关重要。本文将详细介绍儿童拔牙的注意事项&#xff0c;帮助家长们更好地照顾孩子的口腔健康。 1. 拔牙前的准备&#xff1a; 在拔牙前&#xff0c;家长应当向孩子解释拔牙…

Unity打开安卓设备不同的设置面板

1&#xff0c;打开安卓设备不同的设置面板&#xff0c;我还贴心的把Android官网的链接放下面了 2&#xff0c;使用也很方便&#xff1a;unity按钮事件上拖这个脚本&#xff0c;注册MyOpenAndroidSettings方法&#xff0c;参数 填 和枚举值相应的数字 // 功能&#xff1a;打开…

试用NXP官方的UDS bootloader

文章目录 1.前言2.资料获取2.1 MCU例程 2.2 开发环境2.3 上位机2.4 硬件 3.工程修改3.1 boot工程修改 3.2 app工程修改4.测试情况5.例程分享 1.前言 最近很多客户在开发S32K系列MCU时咨询是否可以提供基于UDS协议的bootloader。本文以S32K144为例&#xff0c;介绍如何使用NXP官…

MySQL——系统变量

使用 #最大连接用户数 select MAX_CONNECTIONS; #临时存放构成每次事务的SQL的缓冲区长度 select BINLOG_CACHE_SIZE; #SQL Server的版本信息 select VERSION; 查询结果

云原生测试实战-云计算大数据云原生架构容器技术Kubernetes计算机软件工程软件开发

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …

政安晨:【Keras机器学习示例演绎】(三十六)—— 用聚合注意力增强信念网络

目录 导言 设置和导入 超参数 加载 CIFAR10 数据集 增强层 卷积干 卷积主干 注意力汇集 Patch convnet 回调 学习率时间表 训练 推理 结论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望…

Linux-02

Linux常用命令&#xff1a; ls: 列出目录touch: 创建文件 touch test.txt echo:往文件写内容echo "i love linux" >>test.txtcd&#xff1a;切换目录pwd&#xff1a;显示目前的目录mkdir&#xff1a;创建一个新的目录 mkdir dai:创建目录dai mkdir -p test1/t…

Isaac Sim 6 仅使用isaacsim中自带的工具进行语义分割、实例分割(学习笔记5.09)

一.概要 建立场景&#xff0c;给场景内的物体赋予语义&#xff0c;使用Replicator进行分割操作&#xff0c;从而获得带标签信息的mask掩码图&#xff0c;可作为数据集、验证集等训练使用。 二.具体操作步骤 场景部分 1.搭建一个基础场景 这里建议在搭建的时候就按类别分好类…

L2TP-VPN 专题笔记

笔记连接: 有道云笔记https://note.youdao.com/s/EJBaLwhS 思维导图:

鸿蒙OpenHarmony开发板解析:【 部件配置规则】

部件 部件配置规则 部件的bundle.json放在部件源码的根目录下。以泛sensor子系统的sensor服务部件为例&#xff0c;部件属性定义描述文件字段说明如下&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击…

爬虫爬取必应和百度搜索界面的图片

爬虫爬取必应和百度搜索界面的图片 爬取bing搜索图片界面爬取百度搜索界面图片结果如下 爬取bing搜索图片界面 浏览器驱动下载地址 对应版本即可 浏览器驱动 mad直接用 import os import re from selenium import webdriver from selenium.webdriver import Keys from sel…

ai智能机器人电销的发展现状如何?

在移动互联网时代&#xff0c;人们对于营销的需求越来越高&#xff0c;而传统的营销方式已经无法满足人们的需求。下面我们来看看智能机器人电销的发展现状如何&#xff1f; 智能机器人电销作为一种全新的营销方式&#xff0c;正在迅速崛起。据市场机构统计&#xff0c;未来几…

基于OceanBase+Flink CDC,云粒智慧实时数仓演进之路

摘要&#xff1a;本文整理自云粒智慧高级技术专家付大伟在 4 月 20 日的 2024 OceanBase 开发者大会上的分享&#xff0c;讲述了其数据中台在传统数仓技术框架下做的一系列努力后&#xff0c;跨进 FlinkCDC 结合 OceanBase 的实时数仓演进过程。 内容主要分为以下几个部分: 业务…

武汉星起航:展望跨境电商新篇章,创新发展助力品牌国际化

随着全球经济一体化的深入发展&#xff0c;跨境电商行业正迎来前所未有的发展机遇。在这个充满机遇的时代&#xff0c;武汉星起航电子商务有限公司以其独特的自营亚马逊跨境电商模式和卖家孵化服务&#xff0c;成为了行业内的一股强劲力量。展望未来&#xff0c;武汉星起航将继…
最新文章