LeetCode513. 找树左下角的值

513. 找树左下角的值

文章目录

      • [513. 找树左下角的值](https://leetcode.cn/problems/find-bottom-left-tree-value/)
        • 一、题目
        • 二、题解
          • 方法一:递归法(层序遍历,深度优先搜索)
            • **不足之处以及如何改进**
          • 方法二:迭代


一、题目

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

img

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

示例 2:

img

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,104]
  • -231 <= Node.val <= 231 - 1

二、题解

方法一:递归法(层序遍历,深度优先搜索)

我们可以使用递归来进行深度优先搜索,将每一层的节点值按照层级存储在一个二维向量中,最后返回最底层最左边的节点值。

算法思路:

  1. 使用深度优先搜索(DFS)来遍历整个二叉树。
  2. 在遍历的过程中,我们需要记录每个节点所在的层级,然后将节点值存储在对应的层级中。
  3. 为了达到这个目的,我们可以使用一个二维向量 result,其中 result[i] 存储第 i 层的节点值。
  4. 我们定义一个辅助函数 find,接受三个参数:vec 用来存储节点值,node 当前处理的节点,depth 当前节点的深度。
  5. find 函数中,我们首先检查当前节点是否为空,如果是,则直接返回。
  6. 如果 depth 等于 vec 的大小,说明当前层级还没有被记录,因此需要在 vec 中添加一个新的空向量。
  7. 将当前节点值加入到 vec[depth] 中,然后递归处理左子树和右子树,将深度加一传递下去。
  8. 在主函数 findBottomLeftValue 中,我们首先创建一个空的二维向量 result
  9. 调用 find 函数来遍历二叉树并填充 result
  10. 返回 result 最底层最左边的节点值,即 result[result.size() - 1][0]

具体实现:

class Solution {
public:void find(vector<vector<int>>& vec, TreeNode* node, int depth) {if (node == nullptr) {return;}if (depth == vec.size()) {vec.push_back(vector<int>());}vec[depth].push_back(node->val);find(vec, node->left, depth + 1);find(vec, node->right, depth + 1);}int findBottomLeftValue(TreeNode* root) {vector<vector<int>> result;find(result, root, 0);return result[result.size() - 1][0];}
};

算法分析:

  • 时间复杂度:遍历整个二叉树的时间复杂度为 O(N),其中 N 是二叉树的节点数。在每个节点上,我们进行常数时间的判断、添加和递归操作。
  • 空间复杂度:递归函数的调用会占用栈空间,递归的深度最坏情况下为树的高度,所以空间复杂度为 O(H),其中 H 是二叉树的高度。在最坏情况下,二叉树可能退化为链表,高度为 N,此时空间复杂度为 O(N)。但在一般情况下,二叉树的高度平衡,空间复杂度会接近 O(logN)。
不足之处以及如何改进

上面这个算法在递归过程中对左子树和右子树都会调用 findLeftMostValue 函数,即使在左子树的递归中已经找到了最底层最左边节点,仍然会递归处理右子树。这会导致不必要的重复计算。

改进版算法通过在递归中记录已找到的最大深度 maxDepth,只在当前深度大于最大深度时才更新 resultmaxDepth。这样,如果我们在某一层已经找到了最底层最左边的节点,就不会再继续递归处理右子树,从而减少了不必要的递归操作。

改进版本

class Solution {
public:int findBottomLeftValue(TreeNode* root) {int depth = 0; // 记录当前节点的深度int result = 0; // 记录最底层最左边节点的值findLeftMostValue(root, 1, depth, result);return result;}void findLeftMostValue(TreeNode* node, int currentDepth, int& maxDepth, int& result) {if (!node) {return;}//这里进行了改良,当深度发生变化时result也发生变化,直到最底层if (currentDepth > maxDepth) {result = node->val;maxDepth = currentDepth;}findLeftMostValue(node->left, currentDepth + 1, maxDepth, result);findLeftMostValue(node->right, currentDepth + 1, maxDepth, result);}
};
方法二:迭代

算法思路:

我们也可以使用队列来进行广度优先搜索,逐层遍历二叉树。具体的算法思路如下:

  1. 我们首先创建一个空队列 que,用来存储待处理的节点。
  2. 如果根节点 root 不为空,我们将根节点入队列。
  3. 我们定义一个变量 result 来记录最底层最左边节点的值,初始值设为 0。
  4. 开始循环处理队列中的节点,直到队列为空。
  5. 在每一层遍历中,我们首先获取当前队列的大小 size,即当前层的节点个数。
  6. 然后,我们使用一个循环来处理当前层的所有节点:
    • 弹出队首节点 node,如果是当前层的第一个节点(即 i == 0),我们将其值赋给 result
    • 如果 node 的左子节点不为空,将左子节点入队列。
    • 如果 node 的右子节点不为空,将右子节点入队列。
  7. 当队列为空时,循环结束,此时 result 中存储的就是最底层最左边节点的值,我们将其返回。

具体实现:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int findBottomLeftValue(TreeNode* root) {queue<TreeNode*> que;if (root) que.push(root);int result = 0;while (!que.empty()) {int size = que.size();for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();if (i == 0) {result = node->val; // 记录最后一行第一个元素}if (node->left) {que.push(node->left);}if (node->right) {que.push(node->right);}}}return result;}
};

算法分析:

  • 时间复杂度:遍历整个二叉树的时间复杂度为 O(N),其中 N 是二叉树的节点数。在每个节点上,我们进行常数时间的判断、入队和出队操作。
  • 空间复杂度:队列 que 会占用额外的空间,其大小不会超过二叉树的宽度,因此空间复杂度为 O(W),其中 W 是二叉树的宽度,最坏情况下会达到 O(N)。在一般情况下,二叉树的宽度较小,空间复杂度会接近 O(1)。

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

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

相关文章

Gitignore忽略文件

默认情况下&#xff0c;Git会监视我们项目中的所有内容&#xff0c;但是有些内容比如mode_modules中的内容&#xff0c;我们不希望他被Git所管理。 我们可以在我们项目目录中添加一个 .gitignore 文件来设置那些需要git忽略的文件。

屏幕取色器Mac版_苹果屏幕取色工具_屏幕取色器工具

Sip for Mac 是Mac系统平台上的一款老牌的颜色拾取工具&#xff0c;是设计师和前端开发工作者必不可少的屏幕取色软件&#xff0c;你只需要用鼠标点一下即可轻松地对屏幕上的任何颜色进行采样和编码&#xff0c;并将颜色数据自动存到剪切板&#xff0c;方便随时粘贴出来。 Sip…

GPIO简介

一、GPIO GPIO&#xff08;General-purpose input/output&#xff09;即通用型输入输出&#xff0c;GPIO可以控制连接在其之上的引脚实现信号的输入和输出 芯片的引脚与外部设备相连&#xff0c;从而实现与外部硬件设备的通讯、控制及信号采集等功能 LED实验步骤 实验步骤 以L…

使用Golang反射技术实现一套有默认值的配置解析库

在实际开发中&#xff0c;我们往往会给一个逻辑设计一套配置文件&#xff0c;用于根据不同环境加载不同配置。 比如生产环境和测试环境数据库的地址不一样&#xff0c;我们就需要在配置文件中设置不同的值。但是配置文件中又有一些相同值的配置项&#xff0c;比如数据库的名称等…

Spring事务传播机制、实现方式、失效场景即原理

贴一篇源码分析的好文章&#xff1a;https://blog.csdn.net/qq_30905661/article/details/114400417 本质&#xff1a; 一个事务对应一个数据库连接。 通过 this 来调用某个带有 Transactional 注解的方法时&#xff0c;这个注解是失效的&#xff0c;可以看做这个方法&#x…

Cocos Creator不规则按钮

实现该功能需要用到组件PolygonCollider2D&#xff0c;官方链接&#xff1a; https://docs.cocos.com/creator/3.4/manual/zh/physics-2d/physics-2d-collider.html 创建组件 创建一个精灵节点&#xff1a; 创建碰撞组件PolygonColider2D&#xff0c;如图 给按钮添加多边形碰…

链表的总体涵盖以及无哨兵位单链表实现——【数据结构】

&#x1f60a;W…Y&#xff1a;个人主页 在学习之前看一下美丽的夕阳&#xff0c;也是很不错的。 如果觉得博主的美景不错&#xff0c;博客也不错的话&#xff0c;关注一下博主吧&#x1f495; 在上一期中&#xff0c;我们说完了顺序表&#xff0c;并且提出顺序表中的问题 1. 中…

无涯教程-Lua - 函数声明

函数是一起执行任务的一组语句&#xff0c;您可以将代码分成单独的函数。 Lua语言提供了程序可以调用的许多内置方法。如方法 print()打印在控制台中作为输入传递的参数。 定义函数 Lua编程语言中方法定义的一般形式如下- optional_function_scope function function_name(…

一个SpringBoot 项目能处理多少请求?

这篇文章带大家盘一个读者遇到的面试题哈。 根据读者转述&#xff0c;面试官的原问题就是&#xff1a;一个 SpringBoot 项目能同时处理多少请求&#xff1f; 不知道你听到这个问题之后的第一反应是什么。 我大概知道他要问的是哪个方向&#xff0c;但是对于这种只有一句话的…

Django实现音乐网站 ⑵

使用Python Django框架制作一个音乐网站&#xff0c;在系列文章1的基础上继续开发&#xff0c;本篇主要是后台歌手表模块开发。 目录 表结构设计 歌手表&#xff08;singer&#xff09;结构 创建表模型 设置图片上传路径 创建上传文件目录 生成表迁移 执行创建表 后台管…

代理模式.

前言&#xff1a; 为什么要学习代理模式&#xff0c;因为AOP的底层机制就是动态代理&#xff01; 代理模式&#xff1a; 静态代理 动态代理 静态代理 抽象角色 : 一般使用接口或者抽象类来实现 真实角色 : 被代理的角色 代理角色 : 代理真实角色 ; 代理真实角色后 , 一…

艺术二维码 API 申请及使用

艺术二维码是一种创新的技术产品&#xff0c;它将二维码与美观的背景图像相结合&#xff0c;创造出既实用又美观的作品。它们不仅具有传统二维码的功能性&#xff0c;能被智能设备快速扫描识别&#xff0c;还加入了艺术元素&#xff0c;增强了视觉吸引力和品牌识别度。其中&…

ffmpeg综合应用示例(五)——多路视频合并(Linux版本)

本文的目的为方便Linux下编译运行多路视频合成Demo 原文&#xff1a;ffmpeg综合应用示例&#xff08;五&#xff09;——多路视频合并 Ubuntu 20.04 ffmpeg version ffmpeg-4.4-x86_64 编译 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/home/workspace/dengzr/linux-x64/lib…

jenkins使用gitlab标签发布

关于jenkins git parameter使用gitlab标签发布和分支发布的用法 手动配置的我就不说了&#xff0c;点点点就行&#xff0c;主要是说一下在pipeline里如何使用 通过分支拉取gitlab仓库代码 pipeline {agent anyenvironment {}parameters {gitParameter(branch: , branchFilte…

Sentinel dashboard的使用;Nacos保存Sentinel限流规则

Sentinel dashboard的使用 往期文章 Nacos环境搭建Nacos注册中心的使用Nacos配置中心的使用Sentinel 容灾中心的使用 参考文档 Sentinel alibaba/spring-cloud-alibaba Wiki GitHub 限流结果 下载sentinel-dashboard github地址&#xff1a;Sentinel/sentinel-dashboar…

prototype, construction, instanceof

prototype 属性的作用 JavaScript 规定&#xff0c;每个函数都有一个prototype属性&#xff0c;指向一个对象。 function f() {} typeof f.prototype // "object" ​ 上面代码中&#xff0c;函数f默认具有prototype属性&#xff0c;指向一个对象。 对于普通函数来…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 4

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

C++之观察者模式(发布-订阅)

目录 模式简介 介绍 优点 缺点 代码实现 场景说明 实现代码 运行结果 模式简介 观察者模式&#xff08;Observer Pattern&#xff09;&#xff0c;也叫我们熟知的发布-订阅模式。 它是一种行为型模式。 介绍 观察者模式主要关注的是对象的一对多的关系&#xff0c; …

Go和Java实现组合模式

Go和Java实现组合模式 我们通过部门和员工的层次结构的示例来演示组合模式的用法。 1、组合模式 组合模式&#xff0c;又叫部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对 象&#xff0c;用来表示部分以及整体层次。这种…

【MFC】03.常用复杂控件的使用-笔记

热键&#xff1a; 对话框-类向导&#xff1a;初始化函数中&#xff0c;热键需要在最开始的时候就注册进去&#xff1a; 注册热键&#xff1a; 在这之前&#xff0c;先去定义一个宏&#xff0c;代表你这个快捷键。 参数&#xff1a;窗口句柄&#xff0c;热键编号&#xff08;热…