DAY14|二叉树Part02|LeetCode: 226.翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

news2024/11/25 2:52:01

目录

LeetCode: 226.翻转二叉树

基本思路

C++代码

LeetCode: 101. 对称二叉树

基本思路

C++代码

LeetCode: 104.二叉树的最大深度

基本思路

C++代码

LeetCode: 111.二叉树的最小深度

基本思路

C++代码


LeetCode: 226.翻转二叉树

力扣题目链接

文字讲解:LeetCode: 226.翻转二叉树

视频讲解:听说一位巨佬面Google被拒了,因为没写出翻转二叉树

基本思路

这个题目可以使用递归、迭代以及层序遍历的方法分别解题。

        使用递归的方法,遵循递归三部曲:

  • 确定递归函数的参数和返回值。

        返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*

TreeNode* invertTree(TreeNode* root)
  • 确定终止条件

        当前节点为空的时候,就返回。

if (root == NULL) return root;
  • 确定单层递归的逻辑

        因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。(如果使用后序遍历,则可以先反转左右子树,然后交换左右孩子节点。思考:如果使用中序遍历会怎么样?

//以前序遍历为例
swap(root->left, root->right);//中
invertTree(root->left);//左
invertTree(root->right);//右

        如果直接将swap函数放在交换左右孩子节点的话为什么不可以呢?不妨自己模拟一下交换的过程,以下面的图为例,如果先处理2为中间节点的子树,然后再交换4为中间节点的左右孩子节点,这时2为中间节点的子树就变成了4为中间节点的右子树,此时在对右子树进行处理,实际上还是在对2为中间节点的子树进行处理,显然这样就不能实现题目所想达到的效果(有点拗口,但是自己手写一下过程就很容易弄明白了)。那我们如果想要实现中序遍历实际上可以对左孩子节点进行两次处理,即:

//中序遍历
invertTree(root->left);//左
swap(root->left, root->right);//中
invertTree(root->left);//左

C++代码

//递归法
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};


//迭代法
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        stack<TreeNode*> st;
        st.push(root);
        while(!st.empty()) {
            TreeNode* node = st.top();              // 中
            st.pop();
            swap(node->left, node->right);
            if(node->right) st.push(node->right);   // 右
            if(node->left) st.push(node->left);     // 左
        }
        return root;
    }
};

//层序遍历
class Solution {
public:
    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;
    }
};

LeetCode: 101. 对称二叉树

力扣题目链接

文字讲解:LeetCode: 101. 对称二叉树

视频讲解:新学期要从学习二叉树开始!

基本思路

        首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点,而是要比较两个树(这两个树是根节点的左右子树),比较的是两个子树的里侧和外侧的元素是否相等。

        而这个题目我们依然使用递归的方法进行分析。

  • 确定递归函数的参数和返回值。

因为我们需要比较的两个子树(的里侧和外侧的元素值是否相等),并返回true和false。

bool compare(TreeNode* left, TreeNode* right)
  • 确定终止条件

显然,在判断两个子树是否相等时,存在五种情况:

  1. 左节点为空,右节点不为空,此时返回false。
  2. 左节点不为空,右节点为空,此时返回false。
  3. 左节点为空,右节点也为空,此时返回true。
  4. 左节点不为空,右节点也不为空,但是左右节点的值不相同,此时返回false。
  5. 左节点不为空,右节点也不为空,并且左右节点的值相同,则可以根据此确定单层递归逻辑。
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,因为剩下的情况就是左右值都不为空,并且值相同
  •  确定单层递归逻辑

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

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

C++代码

class Solution {
public:
    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;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;

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



//迭代法
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) 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;
            }

            // 左右一个节点不为空,或者都不为空但数值不相同,返回false
            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;
    }
};

LeetCode: 104.二叉树的最大深度

力扣题目链接

文字讲解:LeetCode: 104.二叉树的最大深度

视频讲解:二叉树的高度和深度有啥区别?究竟用什么遍历顺序?很多录友搞不懂

基本思路

        以递归法为例,本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度,而根节点的高度就是二叉树的最大深度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

这个题我们可以通过后序遍历,求出根节点的高度也就求得了二叉树的最大深度。

依旧是递归法三部曲:

  • 确定递归函数的参数和返回值。

参数传入的是树的根节点,返回值为二叉树的最大深度,为int类型。

int getdepth(TreeNode* node)
  • 确定终止条件

如果是空节点就返回,说明此时高度为零。

if (node == NULL) return 0;
  • 确定单层递归逻辑

先求左子树的深度,再求出右子树的深度,最后取左右子树深度的最大值+1(包含当前中间节点)即为二叉树的深度。

int leftdepth = getdepth(node->left);       // 左
int rightdepth = getdepth(node->right);     // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;

C++代码

class Solution {
public:
    int getdepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};

LeetCode: 111.二叉树的最小深度

力扣题目链接

文字讲解:LeetCode: 111.二叉树的最小深度

视频讲解:看起来好像做过,一写就错!

基本思路

直觉上好像和求最大深度差不多,其实还是差不少的。

本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离也同样是最小深度。

本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:

         需要注意的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。什么是叶子节点呢?左右孩子都为空的节点才是叶子节点!

 熟悉的递归法三部曲:

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

参数需要加入的参数为二叉树的根节点,返回值是二叉树的最小深度,因此为int类型。

int getDepth(TreeNode* node)

确定终止条件

终止条件为直到遇到空节点时停止,此时表示高度为0。

if (node == NULL) return 0;

确定单层递归逻辑

int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);

        求左右子树的深度依旧和前面的方法一样,,但是求最小深度时,我们可以直接将max函数改为min函数吗?如果仔细模拟全过程可以发现其实是不可以的,以下图为例,前面提到左右孩子都为空的节点才是叶子节点,对于根节点1来讲,如果求min(lefDepth,rightDepth),那么会将没有左孩子的分支算为最小深度。此时最小深度为1,而实际上最小深度应该为3。

        因此应该做一个if判断,如果左子树为空且右子树不为空时,最小深度为1+rightDepth;同理如果左子树不为空而右子树为空时,最小深度为1+leftDepth;只有当两边的子树都不为空时,最小深度才是1+min(lefDepth,rightDepth)。

int leftDepth = getDepth(node->left);           // 左
int rightDepth = getDepth(node->right);         // 右
                                                // 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL) { 
    return 1 + rightDepth;
}   
// 当一个右子树为空,左不为空,这时并不是最低点
if (node->left != NULL && node->right == NULL) { 
    return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;

C++代码

class Solution {
public:
    int getDepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftDepth = getDepth(node->left);           // 左
        int rightDepth = getDepth(node->right);         // 右
                                                        // 中
        // 当一个左子树为空,右不为空,这时并不是最低点
        if (node->left == NULL && node->right != NULL) { 
            return 1 + rightDepth;
        }   
        // 当一个右子树为空,左不为空,这时并不是最低点
        if (node->left != NULL && node->right == NULL) { 
            return 1 + leftDepth;
        }
        int result = 1 + min(leftDepth, rightDepth);
        return result;
    }

    int minDepth(TreeNode* root) {
        return getDepth(root);
    }
};


//迭代法
class Solution {
public:

    int minDepth(TreeNode* root) {
        if (root == NULL) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
            int size = que.size();
            depth++; // 记录最小深度
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
                if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
                    return depth;
                }
            }
        }
        return depth;
    }
};

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

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

相关文章

区块链国赛题目--食品溯源(模块三)

区块链国赛题目–食品溯源(模块三) 任务 3-1:区块链应用前端功能开发 1.请基于前端系统的开发模板,在登录组件 login.js、组件管理文件components.js 中添加对应的逻辑代码,实现对前端的角色选择功能,并测试功 能完整性,示例页面如下: 具体要求如下: (1)有明…

学习笔记:Netty网络编程框架

学习视频&#xff1a;Java网络编程教程——Netty深入浅出 参考笔记&#xff1a;网络编程 Netty 前言一、NIO 基础1. NIO三大核心组件1.1 缓冲区 Buffer1.1.1 创建Buffer的方式1.1.2 HeapByteBuffer与DirectByteBuffer1.1.3 Buffer初体验1.1.4 Buffer三个重要参数 1.2 通道 Chan…

牛客网剑指Offer-树篇-JZ27 二叉树的镜像

题目 来源&#xff1a;JZ27 二叉树的镜像 描述 操作给定的二叉树&#xff0c;将其变换为源二叉树的镜像。 数据范围&#xff1a;二叉树的节点数 0≤n≤1000 &#xff0c; 二叉树每个节点的值 0≤val≤1000 要求&#xff1a; 空间复杂度 O(n) 。本题也有原地操作&#xff0c;即…

Axure设置文本——元件动作三

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;设置文本 主要内容&#xff1a;掌握文本框的类型、属性、设置文本赋值的过程 应用场景&#xff1a;各种输入框、数据的重复赋值&#xff1b;多种小…

关于前端程序员使用Idea快捷键配置的说明

相信很多前端程序员 转到后端第一件事就是安装Idea然后学习java&#xff0c;在这里面最难的不是java的语法&#xff0c;而是关于快捷键的修改&#xff0c;前端程序员用的最多的估计就是VsCode或者Webstorm&#xff0c;就拿我自己举例我经常使用Vscode&#xff0c;当我写完代码下…

11-Dockerfile

11-Dockerfile Dockerfile Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 构建步骤&#xff1a; 编写Dockerfile文件docker build命令构建镜像docker run依据镜像运行容器实例 构建过程 Dockerfile编写&#xff1a…

【零售和消费品&存货】食品分类检测系统源码&数据集全套:改进yolo11-RepNCSPELAN_CAA

改进yolo11-goldyolo等200全套创新点大全&#xff1a;食品分类检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.30 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视…

入侵检测算法平台部署LiteAIServer视频智能分析平台行人入侵检测算法

在当今科技日新月异的时代&#xff0c;行人入侵检测技术作为安全防护的重要组成部分&#xff0c;正经历着前所未有的发展。入侵检测算法平台部署LiteAIServer作为这一领域的佼佼者&#xff0c;凭借其卓越的技术实力与广泛的应用价值&#xff0c;正逐步成为守护公共安全的新利器…

命令如诗,步入Linux的晨曦:指令初学者的旅程(下)

文章目录 前言&#x1f99a;补充内容——管道管道的意义示例 &#x1f99a;11. cat - 显示文件内容11.1 显示文件内容11.2 连接多个文件并显示内容11.3 显示行号11.4 合并文件11.5 显示非打印字符11.6 将标准输入输出到文件 &#x1f99a;12. less - 分页查看文件内容12.1 基本…

【安全性分析】正式安全分析与非正式安全分析

安全性分析-系列文章目录 第一章 【安全性分析】正式安全分析与非正式安全分析 第二章 【安全性分析】BAN逻辑 (BAN Logic) 文章目录 安全性分析-系列文章目录前言一、正式安全分析1. 理想化模型(如随机预言机模型)2. 标准模型(Standard Model)3. 形式化验证4. 数学证明二…

kettle工具小经验

1、kettle本地连接数据库报错Error connecting to database: (using class oracle.jdbc.driver.OracleDriver) 原因&#xff1a;缺少jdbc jar包 处理&#xff1a;在data-integration\libswt\win64目录放一个jdbc jar包&#xff0c;我放的是ojdbc6.jar。 不知道为什么&#xff…

Android平台RTSP转RTMP推送之采集麦克风音频转发

技术背景 RTSP转RTMP推送&#xff0c;好多开发者第一想到的是采用ffmpeg命令行的形式&#xff0c;如果对ffmpeg比较熟&#xff0c;而且产品不要额外的定制和更高阶的要求&#xff0c;未尝不可&#xff0c;如果对产品稳定性、时延、断网重连等有更高的技术诉求&#xff0c;比较…

SSM旅游信息系统-计算机毕业设计源码00526

目 录 摘要 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 旅游信息系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4 系统流…

「Mac畅玩鸿蒙与硬件7」鸿蒙开发环境配置篇7 - 使用命令行工具和本地模拟器管理项目

本篇将讲解在 macOS 上配置 HarmonyOS 开发环境的流程&#xff0c;聚焦 hvigorw 命令行工具的使用。我们将以创建 HelloWorld 项目为例&#xff0c;演示使用 hvigorw 进行项目构建、清理操作&#xff0c;并通过 DevEco Studio 的本地模拟器进行预览&#xff0c;帮助提升项目开发…

央国企信创替代,2027年目标百分达成!信创人才评价成标配?

在2027年之前&#xff0c;央国企实现100%的信创替代&#xff0c;标志着中国信息技术应用创新产业发展步入关键阶段。 这一目标不仅体现了国家对于科技自主可控的高度重视&#xff0c;也预示着国内信创产业将迎来前所未有的发展机遇。 一、政策与市场背景 自2020年以来&#xff…

BOE(京东方)全新一代发光器件赋能iQOO 13 全面引领柔性显示行业性能新高度

10月30日,备受瞩目的iQOO最新旗舰机——被誉为“性能之光”的iQOO 13在深圳震撼发布。该款机型由BOE(京东方)独供6.82英寸超旗舰2K LTPO直屏,行业首发搭载全新一代Q10发光器件,在画面表现、护眼舒适度及性能功耗方面均达到行业领先水准,并以“直屏超窄边”的设计为用户呈现了前…

Python Requests 的高级使用技巧:应对复杂 HTTP 请求场景

介绍 网络爬虫&#xff08;Web Crawler&#xff09;是自动化的数据采集工具&#xff0c;用于从网络上提取所需的数据。然而&#xff0c;随着反爬虫技术的不断进步&#xff0c;很多网站增加了复杂的防护机制&#xff0c;使得数据采集变得更加困难。在这种情况下&#xff0c;Pyt…

【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据

【SAP Hana】X-DOC&#xff1a;数据仓库ETL如何抽取SAP中的CDS视图数据 1、无参CDS对应数据库视图2、有参CDS对应数据库表函数3、封装有参CDS为无参CDS&#xff0c;从而对应数据库视图 1、无参CDS对应数据库视图 select * from ZFCML_REP_V where mandt 300;2、有参CDS对应数…

提升网站速度与性能优化的有效策略与实践

内容概要 在数字化快速发展的今天&#xff0c;网站速度与性能优化显得尤为重要&#xff0c;它直接影响用户的浏览体验。用户在访问网站时&#xff0c;往往希望能够迅速获取信息&#xff0c;若加载时间过长&#xff0c;轻易可能导致他们转向其他更为流畅的网站。因此&#xff0…

C#界面设计--9--fatal error C1083: 无法打开包括文件:“jruparse.h”: No such file or directory

1、VS2008-编译时报错“fatal error C1083: 无法打开包括文件:“jruparse.h”: No such file or directory” 2、问题出现的原因及解决方法 1、如果要引入的这些,h文件跟.cpp在同一个目录下&#xff0c;就不会出现这种问题&#xff0c;检査在工程的include目录下是不是真的存…