算法训练 | 二叉树Part5 | 513.找树左下角的值、112.路径总和、106.从中序与后序遍历序列构造二叉树

目录

513.找树左下角的值

递归法

迭代法 ⭐

112.路径总和

递归法

迭代法

106.从中序与后序遍历序列构造二叉树

递归法


513.找树左下角的值

  • 题目链接:513. 找树左下角的值 - 力扣(LeetCode)

  • 文章讲解:programmercarl.com

递归法
  • 解题思路

    • 在树的最后一行找到最左边的值。首先要是最后一行,然后是最左边的值。如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。所以要找深度最大的叶子节点。

    • 那么如何找最左边的呢?可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。

  • 解题步骤

    • 确定递归函数的参数和返回值:参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。

    • 确定终止条件:当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。

    • 确定单层递归的逻辑:在找最大深度的时候,递归的过程中依然要使用回溯。

  • 代码一:递归

class Solution {
public:int maxDepth = INT_MIN;int result;void traversal(TreeNode* root, int depth) {if (root->left == NULL && root->right == NULL) {if (depth > maxDepth) {maxDepth = depth;result = root->val;}return;}if (root->left) {depth++;traversal(root->left, depth);depth--; // 回溯}if (root->right) {depth++;traversal(root->right, depth);depth--; // 回溯}return;}int findBottomLeftValue(TreeNode* root) {traversal(root, 0);return result;}
};
迭代法
  • 解题思路

    • 只需要记录最后一行第一个节点的数值就可以了。

  • 代码一:迭代法

class Solution {
public:int findBottomLeftValue(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) 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;}
};

112.路径总和

  • 题目链接:112.路径总和

  • 文章讲解:代码随想录

  • 113.路径总和ii

递归法
  • 解题思路

    • 要遍历从根节点到叶子节点的路径看看总和是不是目标和。

    • 可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树

  • 解题步骤

    • 确定递归函数的参数和返回类型:

    • 参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。

    • 返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点,本题并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。:

      • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(113.路径总和ii)

      • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (二叉树的最近公共祖先 (opens new window))

      • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

    • 确定终止条件:首先计数器如何统计这一条路径的和,不要去累加然后判断是否等于目标和,代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。如果遍历到了叶子节点,count不为0,就是没找到。

    • 定单层递归的逻辑:因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回

  • 代码一:递归

  • class Solution {
    private:bool traversal(TreeNode* cur, int count) {if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回if (cur->left) { // 左count -= cur->left->val; // 递归,处理节点;if (traversal(cur->left, count)) return true;count += cur->left->val; // 回溯,撤销处理结果}if (cur->right) { // 右count -= cur->right->val; // 递归,处理节点;if (traversal(cur->right, count)) return true;count += cur->right->val; // 回溯,撤销处理结果}return false;}public:bool hasPathSum(TreeNode* root, int sum) {if (root == NULL) return false;return traversal(root, sum - root->val);}
    };
迭代法
  • 解题思路

    • 使用栈模拟递归做回溯,此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。c++就我们用pair结构来存放这个栈里的元素。定义为:pair<TreeNode*, int> pair<节点指针,路径数值>这个为栈里的一个元素。

  • 代码一:迭代法

class solution {public:bool haspathsum(TreeNode* root, int sum) {if (root == null) return false;// 此时栈里要放的是pair<节点指针,路径数值>stack<pair<TreeNode*, int>> st;st.push(pair<TreeNode*, int>(root, root->val));while (!st.empty()) {pair<TreeNode*, int> node = st.top();st.pop();// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回trueif (!node.first->left && !node.first->right && sum == node.second) return true;// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来if (node.first->right) {st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));}// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来if (node.first->left) {st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));}}return false;}
};

106.从中序与后序遍历序列构造二叉树

  • 题目链接:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

  • 文章讲解:代码随想录

  • 105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

递归法
  • 解题思路

    • 以后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。难点是如何切割。应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。

    • 首先要切割中序数组,切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割。

    • 切割后序数组。首先后序数组的最后一个元素不能要,这是切割点也是当前二叉树中间节点的元素,已经用了。后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。此时有一个很重的点,就是中序数组大小一定是和后序数组的大小相同的。中序数组都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

    • 中序数组切成了左中序数组和右中序数组,后序数组切割成左后序数组和右后序数组。接下来可以递归。

  • 解题步骤

    • 第一步:如果数组大小为零的话,说明是空节点了。

    • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

    • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

    • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别反,一定是先切中序数组)

    • 第五步:切割后序数组,切成后序左数组和后序右数组

    • 第六步:递归处理左区间和右区间

  • 代码一:二叉树构造

class Solution {
private:TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {if (postorder.size() == 0) return NULL;// 后序遍历数组最后一个元素,就是当前的中间节点int rootValue = postorder[postorder.size() - 1];TreeNode* root = new TreeNode(rootValue);// 叶子节点if (postorder.size() == 1) return root;// 找到中序遍历的切割点int delimiterIndex;for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {if (inorder[delimiterIndex] == rootValue) break;}// 切割中序数组// 左闭右开区间:[0, delimiterIndex)vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);// [delimiterIndex + 1, end)vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );// postorder 舍弃末尾元素postorder.resize(postorder.size() - 1);// 切割后序数组// 依然左闭右开,注意这里使用了左中序数组大小作为切割点// [0, leftInorder.size)vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());// [leftInorder.size(), end)vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());root->left = traversal(leftInorder, leftPostorder);root->right = traversal(rightInorder, rightPostorder);return root;}
public:TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {if (inorder.size() == 0 || postorder.size() == 0) return NULL;return traversal(inorder, postorder);}
};

(说明:基于代码随想录课程学习,部分内容引用代码随想录文章)

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

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

相关文章

超声波清洗机哪些品牌好用点?四大极其出色的机型一目了然

各位眼镜侠们&#xff0c;在佩戴眼镜的是&#xff0c;有没有觉得眼镜总是有些难以言喻的“味道”或者是污渍在镜片上面。是的&#xff0c;没有猜错&#xff0c;那是我们脸上油脂、汗液和各种不明物质的混合体。特别是在夏天的时候天气太炎热会经常出汗&#xff0c;眼镜上会沾染…

2021职称继续教育--加快构建完整内需体系,形成国内国际双循环相互促进新格局

单选题&#xff08;共7题&#xff0c;每题5分&#xff09; 1、根据本讲&#xff0c;突破和推进“一带一路”战略&#xff0c;要满足以企业为主体、以&#xff08;&#xff09;为导向的基本要求。 D、市场 2、根据本讲&#xff0c;让农村消费市场持续扩张的前提&#xff08;&am…

shell将文件分割成小块文件

背景&#xff1a;某软件最多支持1G的文件传输&#xff0c;需要对大文件进行切割。 方案1&#xff1a; 可以使用split命令将文件均分成10分片。以下是具体的命令示例&#xff1a; split -b $(($(du -b < 文件名) / 10)) 文件名 分片前缀 这里文件名是你想要分割的文件的名…

网络架构三层到大二层的对比和选择

在企业的网络结构选择中&#xff0c;有二层网络和三层网络结构两种选择。三层是按照逻辑拓扑结构进行的分类&#xff0c;汇聚层和接入层&#xff0c;流量纵向经过接入层、汇聚层网络&#xff0c;收敛至骨干核心层。二层网络结构没有汇聚层。大二层网络架构通常使用VLAN&#xf…

上海冠珠旗舰总店盛装开业暨冠珠瓷砖中国美学设计巡回圆满举办

上海&#xff0c;这座融合了东西方文化的国际化大都市&#xff0c;不仅是中国的时尚中心&#xff0c;也是全球潮流的汇聚地。在这里&#xff0c;古典与现代交织&#xff0c;传统与前卫并存&#xff0c;为传统色彩与现代设计的融合提供了得天独厚的条件。 5月25日&#xff0c;上…

JWT-登录后下发令牌

后端 写一个jwt工具类&#xff0c;处理令牌的生成和校验&#xff0c;如&#xff1a; 响应数据样例&#xff1a; 前端要做的&#xff1a;

ts 中的 type 和 interface 有什么区别?

一、用法举例 interface Person {name: stringage: number }const person: Person {name: Kite,age: 24 }type Person {name: stringage: number }const person: Person {name: Kite,age: 24 }二、翻阅 ts 的官方文档&#xff1a; 1、interface 接口 TypeScript的核心原则…

Weblogic SSRF漏洞 [CVE-2014-4210]

漏洞复现环境搭建请参考 http://t.csdnimg.cn/svKal docker未能成功启动redis请参考 http://t.csdnimg.cn/5osP3 漏洞原理 Weblogic的uddi组件提供了从其他服务器应用获取数据的功能并且没有对目标地址做过滤和限制&#xff0c;造成了SSRF漏洞&#xff0c;利用该漏洞可以向内…

【AJAX前端框架】Asynchronous Javascript And Xml

1 传统请求及缺点 传统的请求都有哪些&#xff1f; 直接在浏览器地址栏上输入URL。点击超链接提交form表单使用JS代码发送请求 window.open(url)document.location.href urlwindow.location.href url… 传统请求存在的问题 页面全部刷新导致了用户的体验较差。传统的请求导…

安泰电子:高压功率放大器应用场合介绍

高压功率放大器是一种电子设备&#xff0c;用于将低电压信号放大到较高电压水平&#xff0c;以满足各种应用需求。它在多个领域中具有广泛的应用&#xff0c;包括科学研究、工业生产、通信技术以及医疗设备。下面安泰电子将介绍高压功率放大器的应用场合。 科学研究 高压功率放…

【最优化方法】实验一 熟悉MATLAB基本功能

实验一  熟悉MATLAB基本功能 实验的目的和要求&#xff1a;在本次实验中&#xff0c;通过亲临使用MATLAB&#xff0c;对该软件做一全面了解并掌握重点内容。 实验内容&#xff1a; &#xff11;、全面了解MATLAB系统 &#xff12;、实验常用工具的具体操作和功能 学习建…

在Open AI的Assistant API中,Thread代表什么?

在OpenAI的Assistant API中&#xff0c;Thread通常代表一系列相关的对话&#xff0c;保持对话的上下文和连贯性。这对于创建连续对话非常重要&#xff0c;因为它允许模型记住先前的交互&#xff0c;并在随后的响应中参考这些信息。 具体作用 保持上下文&#xff1a;Thread可以…

深入学习Python:掌握面向对象编程

在上一篇文章中,我们介绍了Python的基本语法和概念,包括变量、数据类型、条件语句、循环、函数和文件操作。接下来,我们将深入探讨Python的面向对象编程(OOP)特性,这是现代编程中的一个重要概念。通过这篇文章,你将学会如何使用类和对象来组织和管理你的代码。 1. 面向…

哇!数据中台竟是企业数字化转型的关键力量!

在当今数字化浪潮席卷的时代&#xff0c;数据中台正成为企业实现数字化转型的关键力量。那么&#xff0c;究竟什么是数据中台呢&#xff1f;它乃是一种持续让企业数据活跃起来的机制&#xff0c;能够将企业内各部分数据汇聚至一个平台&#xff0c;达成数据的统一化管理。 数据中…

Linux快速定位日志 排查bug技巧和常用命令

1. 快速根据关键字定位错误信息 grep 在 Linux 系统中&#xff0c;可以使用 grep 命令来查找日志文件中包含特定关键字的行。假设你的日志文件路径为 /var/log/myapp.log&#xff0c;你想要查找包含关键字 "abc" 的日志内容&#xff0c;可以按照以下步骤操作&#…

机器学习中的距离公式

1. 欧式距离 (Euclidean Distance) 公式: [ d ( p , q ) = ∑ i = 1 n ( p i − q i

Intel base instruction -- cmove

Conditional Move&#xff1b;以操作码&#xff08;条件码&#xff09;区分不同的移动条件。 opcode 以 0F 4* 打头&#xff1b; /*509a: eb 0b jmp 50a7 <__sprintf_chkplt0x2937> 509c: 0f 1f 40 00 nopl 0x0(%rax)*/…

TIM(Timer)简介

TIM&#xff08;Timer&#xff09;定时器介绍 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时不仅具备基本的定时中断功能&#xff0c;而且…

maven的下载以及配置的详细教程(附网盘下载地址)

文章目录 下载配置IDEA内部使用配置 下载 1.百度网盘下载 链接: https://pan.baidu.com/s/1LD9wOMFalLL49XUscU4qnQ?pwd1234 提取码: 1234 2.解压即可 配置 1.打开安装文件下conf下的settings.xml文件&#xff0c;我的如下 2.修改配置信息&#xff08;目的是为了修改本地…