LeetCode题解 二叉树(五):226 翻转二叉树;101 对称二叉树;100 相同的树;572 另一个树的子树

news2024/12/25 17:02:26

226 翻转二叉树 easy

这道题有一段广为人知的传说:曾有人说Homebrew(适用于macOS和Linux的开源软件包管理器)的作者Max Howell,没有在白板上写出这道题目,被Google拒绝了。

至于是不是真的因为没做出来这道题就被拒绝,我想只有面试官自己本人清楚了。

回到正题,翻转二叉树的意思,就是将一棵数的左右子树完全交换

什么叫做完全交换呢?参考下图,不仅2与7发生了交换,且7的左右子树6和9也发生了交换。

226.翻转二叉树

此前我们讲完了二叉树的深度遍历和层序遍历。也是从这道题开始,之后的题目,大多情况下我们都需要想一想应该用那种遍历方式。

那么这道题应该用哪种呢?

我首先想到的是层序遍历,将每一层每两个节点做一次交换就可以。

或者也可以用前序遍历或者后序遍历,因为在这两种遍历中,左右节点是依次处理的。中序遍历却不可以。

所以这道题,可以用深度优先的递归法和迭代法 +广度优先遍历的迭代法,三种方法来解决,颇有种三英战吕布(easy level)的意味。

深度优先,递归法——

void reverse(TreeNode* cur) {
    if (!cur) return;
    swap(cur->left, cur->right);
    reverse(cur->left);
    reverse(cur->right);
}
TreeNode* invertTree(TreeNode* root) {
    if (!root) return root;
    reverse(root);
    return root;
}

还有一种更加简洁的写法,直接调用本函数完成递归,代码如下:

TreeNode* invertTree(TreeNode* root) {
    if (!root) return root;
    swap(root->left, root->right);
    invertTree(root->left);
    invertTree(root->right);
    return root;
}

深度优先,迭代法——

刚刚提到使用前序或者后序遍历都可以,此处我们就使用前序遍历来完成,代码如下:

TreeNode* invertTree(TreeNode* root) {
    stack<TreeNode*> stk;
    if (root) stk.push(root);
    while (!stk.empty()) {
        TreeNode *cur = stk.top();
        stk.pop();
        swap(cur->left, cur->right);
        if (cur->left) stk.push(cur->left);
        if (cur->right) stk.push(cur->right);
    }
    return root;
}

此前我们也跟着随想录,走了一遍如何使用统一写法(基于标记法),完成三种遍历,所以此处也附上使用统一写法的前序遍历,代码如下——

TreeNode* invertTree(TreeNode* root) {
    stack<TreeNode*> stk;
    if (root) stk.push(root);
    while (!stk.empty()) {
        TreeNode *cur = stk.top();
        // 统一写法中最关键的地方
        if (cur) {
            stk.pop();
            if (cur->right) stk.push(cur->right);
            if (cur->left) stk.push(cur->left);
            stk.push(cur);
            stk.push(nullptr);
        } else {
            stk.pop();
            cur = stk.top();
            stk.pop();
            swap(cur->left, cur->right);
        }
    }
    return root;
}

最后一种方法,就是广度优先遍历,即层序遍历,代码如下:

TreeNode* invertTree(TreeNode* root) {
    queue<TreeNode*> que;
    if (root != NULL) que.push(root);
    while (!que.empty()) {
        int size = que.size();
        for (int i = 0; i < size; i++) {
            TreeNode* node = que.front();
            que.pop();
            swap(node->left, node->right); // 节点处理
            if (node->left) que.push(node->left);
            if (node->right) que.push(node->right);
        }
    }
    return root;
}

101 对称二叉树 easy

这道题教会了我们对称二叉树的定义,见下图即可:

101. 对称二叉树

首先判断应该用那种遍历方式。对称,从感觉上讲层序遍历可以解决问题,但对每一层的值进行判断,会相当耗费时间。

那么这道题可以从深度遍历的角度考虑,前中后序应该用哪一种又成了问题。

其实这道题只能用“后序”遍历,因为对于这道题而言,是否为对称,要先看左右子树是否符合条件,根节点的优先级在最后。为什么要给后序加一个引号呢?是因为这道题已经并非是正统的后序遍历了。我们要对左右子树进行判断,那么比较的对象就是:

左结点的左孩子与右结点的右孩子,左节点的右孩子和右结点的左孩子,顺序是相反的。

这道题可以使用递归,迭代来完成。

递归法要通过返回比较结果,所以返回值是bool类型;那么结束条件就分四种:

{左右都为空,返回true},{左右其中一个不存在,返回false},{左右值都存在,但是却不相同,返回false},那么最终剩下的结果,就是左右都存在且值相同。

递归法的代码(简洁版)如下:

bool compare(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;
    else return compare(left->left, right->right) && compare(left->right, right->left);
}

bool isSymmetric(TreeNode* root) {
    if (root == NULL) return true;
    return compare(root->left, root->right);
}

那么使用迭代法呢?刚刚我们说过了这道题并非正统的后序遍历,所以遍历顺序自然要符合题意。迭代法我们首先会想到使用栈,但这道题因为每两个节点就要比较一次,所以使用队列也是可以的,在代码中用哪种都可以,此处我们使用队列,代码如下:

bool isSymmetric(TreeNode* root) {
    if (!root) return true;
    queue<TreeNode*> que;   
    que.push(root->left);
    que.push(root->right);
    while (!que.empty()) {
        TreeNode *leftNode = que.front();
        que.pop();
        TreeNode *rightNode = que.front();
        que.pop();
        if (!leftNode && !rightNode) continue;
        if (!leftNode || !rightNode || (leftNode->val != rightNode->val))
            return false;
        // 注意入队顺序
        que.push(leftNode->left);
        que.push(rightNode->right);
        que.push(leftNode->right);
        que.push(rightNode->left);
    }
    return true;
}

接下来提到的这两道题,属于对称二叉树的变种,稍微修改一下就可以AC,所以也就不再深入的理解了。

100 相同的树 easy

检测两棵数是否相同,方法从比较一棵树,变成了真的比较“左右两棵子树”

递归法代码如下:

bool compare(TreeNode *cur1, TreeNode *cur2) {
    if (cur1 == nullptr && cur2 == nullptr) return true;
    else if (cur1 != nullptr && cur2 == nullptr) return false;
    else if (cur1 == nullptr && cur2 != nullptr) return false;
    else if (cur1->val != cur2->val) return false;
    else return compare(cur1->left, cur2->left) && compare(cur1->right, cur2->right);
}

bool isSameTree(TreeNode* p, TreeNode* q) {
    return compare(p, q);
}

572 另一个树的子树 easy

判断一棵树中是否包含另一棵树,见下图:

img

同样使用迭代法,代码如下:

bool compare(TreeNode* left, TreeNode* right) { 
    if (left == nullptr && right != nullptr) return false;
    else if (left != nullptr && right == nullptr) return false;
    else if (left == nullptr && right == nullptr) return true;
    else if (left->val != right->val) return false;

    return compare(left->left, right->left) && compare(left->right, right->right);
}

bool isSubtree(TreeNode* root, TreeNode* subRoot) {
    if (root == nullptr) return false;

    return compare(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}

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

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

相关文章

深入浅出JVM之执行引擎的解释执行与编译执行

本篇文章围绕执行引擎&#xff0c;深入浅出的解析执行引擎中解释器与编译器的解释执行和编译执行、执行引擎的执行方式、逃逸分析带来的栈上分配、锁消除、标量替换等优化以及即时编译器编译对热点代码的探测 执行引擎 hotspot执行引擎结构图 执行引擎分为解释器、JIT即时编译…

大数据框架Hadoop篇之Hadoop入门

1. 写在前面 今天开始&#xff0c;想开启大数据框架学习的一个新系列&#xff0c;之前在学校的时候就会大数据相关技术很是好奇&#xff0c;但苦于没有实践场景&#xff0c;对这些东西并没有什么体会&#xff0c;到公司之后&#xff0c;我越发觉得大数据的相关知识很重要&…

Jmeter(二十二):硬件性能监控指标

硬件性能监控指标 一、性能监控初步介绍 性能测试的主要目标 1.在当前的服务器配置情况&#xff0c;最大的用户数 2.平均响应时间ART&#xff0c;找出时间较长的业务 3.每秒事务数TPS&#xff0c;服务器的处理能力 性能测试涉及的内容 1.客户端性能测试&#xff1a;web前…

洛谷——【入门2】分支结构

文章目录题单简介【深基1-2】小学数学 N 合一题目描述问题 1问题 2问题 3问题 4问题 5问题 6问题 7问题 8问题 9问题 10问题 11问题 12问题 13问题 14输入格式输出格式样例 #1样例输入 #1样例输出 #1提示AC代码【深基2.习6】Apples Prologue / 苹果和虫子题目描述输入格式输出格…

尝鲜:SpreadJS-en已出 16.0 SpreadJS-cn 16.0-23年1月出

此次版本更新将带来众多的增强功能&#xff0c;而其中新的文件结构尤为重要&#xff0c;是近几个版本中最重要的架构级更新&#xff01; 其设计目标是 *减少 SSJSON 的体积&#xff0c;平均减小到原来 30% *提供按需加载&#xff08;Lazyload&#xff09;能力&#xff0c;相对…

【学习】https://gitee.com/DingJiaxiong

【学习】https://gitee.com/DingJiaxiong 文章目录【学习】<https://gitee.com/DingJiaxiong>0 前言1 Java SE2 Java Web3 Maven基础4 Git5 SSM框架6 MybatisPlus7 SpringBoot0 前言 事情是这样&#xff0c;我准备把之前所有的笔记都放到Gitee 上了 不用GitHub … 就别问…

Spring的AOP切面应用对【后台对接口增强】

目录&#x1f4da;简介&#xff1a;&#x1f4a8;切面表达式&#xff1a;&#x1f4ad;切面通知类型&#xff1a;&#x1f5fa;️创建项目演示&#xff1a;&#x1f3a2;创建项目&#xff1a;&#x1f383;添加依赖&#xff1a;&#x1f4a8;编写切面类&#xff1a;&#x1f68…

前端显示分页详解

我们在浏览页面的时候&#xff0c;是不是经常看到网页经常是以页面的形式给我们展现出来的&#xff0c;我们以淘宝的页面为例&#xff08;如下图&#xff09;&#xff0c;那这样按照页面呈现有什么好处呢&#xff1f;这篇文章让我们来深入探究了解这其中的奥秘。 优点&#xff…

泛型自动装箱

目录 前言 泛型 1.泛型的目的 2.泛型存在的意义和注意事项&#xff1a; 3.擦除机制 4.泛型的边界 5.泛型方法&#xff1a; 包装类&#xff1a; 前言 只要知道《》是啥意思&#xff0c;其他了解即可 泛型的上界 通配符简单知道就行 泛型 1.泛型的目的 指定当前的容器&am…

【JVM】浅析程序计数器与虚拟机栈

文章目录1. 程序计数器2. 虚拟机栈3. 栈内存溢出1. 程序计数器 Program Counter Register 程序计数器&#xff08;寄存器&#xff09; 程序计数器的作用是什么&#xff1f; 是记录下一条JVM指令的执行地址行号 程序计数器有什么特点&#xff1f; 线程私有的不会存在内存溢出 …

ADI Blackfin DSP处理器-BF533的开发详解59:DSP控制ADXL345三轴加速度传感器的应用2(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 MEMS三轴加速度传感器 我做了一个三轴加速度传感器的子卡&#xff0c;插在这个板子上&#xff0c;然后写了一些有意思的应用程序。 硬件实现原理…

Linux——定制Linux

Linux启动流程 首先&#xff0c;Linux要通过自检&#xff0c;检查硬件设备有没有故障如果有多块启动盘的话&#xff0c;需要在BIOS选择启动磁盘启动MBR中的bootloader引导程序加载内核文件执行所有进程的父进程、老祖宗systemd欢迎界面 加载内核文件的关键文件 kernel文件&a…

C#调用Python脚本训练并生成AI模型(以Paddle框架为例)

目录一. IronPython语言移植1.1 IronPython安装1.2 示例代码1.3 运行结果1.4 特点二. C#调用Python文件打包dll2.1 步骤2.2 限制三. C#命令行调用.py文件执行3.1 代码3.3 运行结果3.4 特点四. C#调用Python可执行exe4.1 步骤4.1.1 使用pyinstaller打包python程序4.1.2 在c#中调…

入门:手动构建镜像

前面我们已经了解了Docker镜像的结构&#xff0c;实际上所有常用的应用程序都有对应的镜像&#xff0c;我们只需要下载这些镜像然后就可以使用了&#xff0c;而不需要自己去手动安装&#xff0c;顶多需要进行一些特别的配置。当然要是遇到某些冷门的应用&#xff0c;可能没有提…

【细胞分割】中值滤波+分水岭法细胞计数【含Matlab源码 640期】

⛄一、图像分割简介 理论知识参考&#xff1a;【基础教程】基于matlab图像处理图像分割【含Matlab源码 191期】 ⛄二、部分源代码 clear; close all; %------------------ %程序中定义图像变量说明 %Image->原图变量; %Image_BW->二值化图象; %Image_BW_medfilt->中…

【实时数仓】DWM层跳出明细计算之需求分析、读取数据、通过Flink的CEP完成跳出判断、写回kafka、测试

文章目录一 DWM层-跳出明细计算1 需求分析与思路&#xff08;1&#xff09;什么是跳出&#xff08;2&#xff09;计算跳出行为的思路&#xff08;3&#xff09;实现思路2 读取数据&#xff08;1&#xff09;代码编写&#xff08;2&#xff09;测试3 通过Flink的CEP完成跳出判断…

【MATLAB100个实用小技巧】——数值分析(85-100)

文章目录前言系列文章85.86. 三次样条插值法87. NEWTON 插值88. hermite 插值89. newton 形式的 hermite 插值90. 平方根法91. gauss 消去法92. 三角分解法93. jacobi 迭代法94. gauss 迭代法95. sor 迭代法96. 最速下降法97. 共额梯度法98. newton 迭代法99. broyden 迭代法10…

前端媒体查询@media示例详解和calc()函数的使用

媒体查询media media 可以针对不同的屏幕尺寸设置不同的样式&#xff0c;特别是如果需要设置设计响应式的页面&#xff0c;media 是非常有用的。当重置浏览器大小的过程中&#xff0c;页面也会根据浏览器的宽度和高度重新渲染页面。 eg&#xff1a;如果文档宽度小于 500 像素…

pytorch 自编码器实现图像的降噪

自编码器 自动编码器是一种无监督的深度学习算法&#xff0c;它学习输入数据的编码表示&#xff0c;然后重新构造与输出相同的输入。它由编码器和解码器两个网络组成。编码器将高维输入压缩成低维潜在(也称为潜在代码或编码空间) &#xff0c;以从中提取最相关的信息&#xff…

SpringCloud之Hystrix

复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候将不可避免失败&#xff01; 服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务&#xff0c;这就是所谓的“…