DAY16|二叉树Part04|LeetCode: 513.找树左下角的值、112. 路径总和、106. 从中序与后序遍历序列构造二叉树

目录

LeetCode: 513.找树左下角的值

基本思路

C++代码

LeetCode: 112. 路径总和、113.路径总和II

LeetCode: 112. 路径总和

C++代码

LeetCode: 113.路径总和II

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

基本思路

C++代码


LeetCode: 513.找树左下角的值

力扣代码链接

文字讲解:LeetCode: 513.找树左下角的值

视频讲解:怎么找二叉树的左下角? 递归中又带回溯了,怎么办?

基本思路

        对题目进行一下分析,要找二叉树最底层最左边节点的数值。首先要是最后一行,然后是最左边的值。如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。

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

        注意:有一个地方容易误解,即二叉树最底层最左边的节点不一定是左孩子,也可能是右孩子!!!

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

        首先需要遍历根节点,以及定义一个变量记录深度为int类型,返回值可以为void。本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。

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;

C++代码

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;}
};

LeetCode: 112. 路径总和、113.路径总和II

力扣代码链接:112. 路径总和、113.路径总和II

文字讲解:LeetCode: 112. 路径总和、113.路径总和II

视频讲解:拿不准的遍历顺序,搞不清的回溯过程,我太难了!

LeetCode: 112. 路径总和

基本思路

        这道题我们要遍历从根节点到叶子节点的路径看看总和是不是目标和。可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树。

        如下图所示,这里明显要采用回溯算法,如果当前路径不符合,我们就从倒数第二天路还是回溯,还是不行就回溯倒数第三、倒数第四条路,直到找到合适的路径或者全部搜索完成。

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

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

        返回值:我们应该是只要找一条符合条件的路径,递归函数就需要及时返回,那么返回类型是bool。

bool traversal(treenode* cur, int count)   // 注意函数的返回类型
  • 确定终止条件

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

if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找到合适的边,直接返回
  • 确定单层递归的逻辑

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

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;

C++代码

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);}
};

LeetCode: 113.路径总和II

基本思路

113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!

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

参数:依旧需要遍历所有的根节点,并且使用count记录目标和。

返回值:返回值为void,因为我们要遍历整个树,然后记录路径,所以递归函数要不返回值

注意:本题要求我们求出整条路径,因此我们需要一个数组记录所有路径,另外还需要一个数组记录所有满足条件的路径。

vector<vector<int>> result;
vector<int> path;
void traversal(TreeNode* cur, int count)
  • 确定终止条件

确定终止条件,直接跟之前一样即可

if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径result.push_back(path);return;
}
if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回
  • 确定单层递归的逻辑
if (cur->left) { // 左 (空节点不遍历)path.push_back(cur->left->val);count -= cur->left->val;traversal(cur->left, count);    // 递归count += cur->left->val;        // 回溯path.pop_back();                // 回溯
}
if (cur->right) { // 右 (空节点不遍历)path.push_back(cur->right->val);count -= cur->right->val;traversal(cur->right, count);   // 递归count += cur->right->val;       // 回溯path.pop_back();                // 回溯
}
return ;

C++代码

class solution {
private:vector<vector<int>> result;vector<int> path;// 递归函数不需要返回值,因为我们要遍历整个树void traversal(TreeNode* cur, int count) {if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径result.push_back(path);return;}if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回if (cur->left) { // 左 (空节点不遍历)path.push_back(cur->left->val);count -= cur->left->val;traversal(cur->left, count);    // 递归count += cur->left->val;        // 回溯path.pop_back();                // 回溯}if (cur->right) { // 右 (空节点不遍历)path.push_back(cur->right->val);count -= cur->right->val;traversal(cur->right, count);   // 递归count += cur->right->val;       // 回溯path.pop_back();                // 回溯}return ;}public:vector<vector<int>> pathSum(TreeNode* root, int sum) {result.clear();path.clear();if (root == NULL) return result;path.push_back(root->val); // 把根节点放进路径traversal(root, sum - root->val);return result;}
};

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

力扣代码链接

文字讲解:LeetCode: 106. 从中序与后序遍历序列构造二叉树

视频讲解:坑很多!来看看你掉过几次坑

基本思路

        首先回忆一下如何根据两个顺序构造一个唯一的二叉树,相信理论知识大家应该都清楚,就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。

把步骤量化出来:

(第一步:如果数组大小为零的话,说明是空节点了。)
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间

可以根据步骤写出整体框架:

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;}// 第四步:切割中序数组,得到 中序左数组和中序右数组// 第五步:切割后序数组,得到 后序左数组和后序右数组// 第六步root->left = traversal(中序左数组, 后序左数组);root->right = traversal(中序右数组, 后序右数组);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);

C++代码

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.xdnf.cn/news/3190.html

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

相关文章

【算力基础】GPU算力计算和其他相关基础(TFLOPS/TOPS/FP32/INT8...)

文章目录 :one: 算力的常见指标:two: 算力计算:three: 常用链接 &#x1f680; 本文主要是聚焦于深度学习领域的 GPU的算力估计&#xff0c;其他类型的硬件设备如CPU可以类比参考。 1️⃣ 算力的常见指标 算力衡量主要与运算速度和精度这两个指标有关。 &#x1f314;速度指…

云集电商:如何通过 OceanBase 实现降本 87.5%|OceanBase案例

云集电商&#xff0c;一家聚焦于社交电商的电商公司&#xff0c;专注于‘精选’理念&#xff0c;致力于为会员提供超高性价比的全品类精选商品&#xff0c;以“批发价”让亿万消费者买到质量可靠的商品。面对近年来外部环境的变化&#xff0c;公司对成本控制提出了更高要求&…

认定出现不安全行为并通过系统发出告警信息的名厨亮灶开源了。

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

yolov5快速复现

源码下载 进入github官网搜索yolov5(网址&#xff1a;https://github.com/ultralytics/yolov5)如下图红框所示&#xff1a; 进入界面如下图所示&#xff0c;点击右上角绿色Code&#xff0c;选择下载压缩包&#xff1a; 开发环境设置 本文使用云平台开发&#xff0c;系统为Li…

“再探构造函数”(2)

文章目录 一. 友元‘全局函数’作友元‘成员函数’作友元‘类‘作友元 二. 内部类三. 匿名对象四. 对象拷贝时的编译器优化分析调用时的顺序 一. 友元 何时会用到友元呢&#xff1f; 当想让&#xff08;类外面的某个函数/其它的类&#xff09;访问 某个类里面的(私有或保护的…

一周内从0到1开发一款 AR眼镜 相机应用?

目录 1. &#x1f4c2; 前言 2. &#x1f4a0; 任务拆分 2.1 产品需求拆分 2.2 开发工作拆分 3. &#x1f531; 开发实现 3.1 代码目录截图 3.2 app 模块 3.3 middleware 模块 3.4 portal 模块 4. ⚛️ 拍照与录像 4.1 前滑后滑统一处理 4.2 初始化 View 以及 Came…

Redis(2):内存模型

一、Redis内存统计 工欲善其事必先利其器&#xff0c;在说明Redis内存之前首先说明如何统计Redis使用内存的情况。 在客户端通过redis-cli连接服务器后&#xff08;后面如无特殊说明&#xff0c;客户端一律使用redis-cli&#xff09;&#xff0c;通过info命令可以查看内存使用情…

2024年CG作品网站推荐(精选篇)

ArtStation https://www.artstation.com GGAC https://www.ggac.com/

GraphRAG如何构建知识图谱Knowledge Graph

GraphRAG工作的第一步&#xff0c;是将输入的文档集合&#xff0c;按一定的策略拆分成一个一个chunks&#xff0c;然后解析每个chunks&#xff0c;将chunk中所关注的实体(entity)和关系(relation)解析出来&#xff0c;以此构建知识图谱。 那问题来了&#xff0c;GraphRAG是如何…

Spring Security 框架篇-深入了解 Spring Security 的认证功能流程和自定义实现登录接口(实现自定义认证过滤器、登出功能)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Spring Security 框架概述 2.0 Spring Security 核心功能-认证功能 2.1 过滤器链 2.2 登录认证流程 2.3 思路分析 3.0 登录认证具体操作 3.1 环境搭建 3.2 实现 U…

JavaScript基础语法部分-黑马跟课笔记

一、Javascript介绍 1.JavaScript是什么&#xff1f; 1.是什么&#xff1f; 是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互效果 2.作用&#xff08;做什么&#xff1f;&#xff09; 网页特效&#xff08;监听用户的一些行为让网页做…

qt QDir详解

1、概述 QDir是Qt框架中的一个核心类&#xff0c;它提供了对文件系统目录的操作接口。Qt是一个跨平台的应用程序开发框架&#xff0c;广泛用于开发桌面、移动和嵌入式设备上的应用程序。QDir类使得开发者能够方便地在不同操作系统上处理目录和文件&#xff0c;如进行目录遍历、…

Jwt加解密

概述 记录jwt加解密的demo。 JSON Web Token (JWT) 是一种开放标准 (RFC 7519)&#xff0c;用于在网络应用环境间安全地传输信息。JWT 通常用于身份验证和信息交换&#xff0c;因为它可以被签名和加密&#xff0c;确保数据的完整性和隐私性。 JWT 的基本结构 JWT 由三部分组…

鸥柏(OBOO)户外触摸广告屏科技创新 高速服务区收费站案例

鸥柏&#xff0c;作为户外液晶显示技术的品牌高端领先者&#xff0c;其新产品鸥柏户外触摸屏在高速服务区收费站入口处得到了真实且广泛的应用。OBOO鸥柏户外广告机能够存储和展示海量信息&#xff0c;包括新闻、政策、天气预报、实时路况等&#xff0c;为过往司乘人员提供丰富…

ASED6015SH-ASEMI中低压MOS管ASED6015SH

ASED6015SH-ASEMI中低压MOS管ASED6015SH 型号&#xff1a;ASED6015SH 品牌&#xff1a;ASEMI 导通内阻&#xff1a;90mΩ 启动电压&#xff1a;2V-4V 最大漏源电流&#xff08;Id&#xff09;&#xff1a;19A 漏源击穿电压&#xff08;VRM&#xff09;&#xff1a;150V …

`掌握Python-PPTX,让PPt制作变得轻而易举!`

文章目录 掌握Python-PPTX&#xff0c;让PPT制作变得轻而易举&#xff01;背景介绍python-pptx 是什么&#xff1f;如何安装 python-pptx&#xff1f;简单库函数使用方法应用场景常见Bug及解决方案总结 掌握Python-PPTX&#xff0c;让PPT制作变得轻而易举&#xff01; 背景介绍…

linux的用户账号与权限管理

一、用户账号 root 和zhang 表示当前的登录用户test1 表示当前的主机名/home&#xff1a; 表示当前所在的目录为/home~&#xff1a; 表示当前所在的目录为~#&#xff1a; 表示当前用户是管理员$&#xff1a; 表示当前用户是普通用户 1.切换用户 su - 用户名 &#xff08;完全切…

Qt项目实战:银行利息(贷款)计算器

目录 一.ui设计 二.初始化表单 三. 存款计算 四.贷款计算 五.效果 六.代码 1.h 2.cpp 一.ui设计 二.初始化表单 获取当前时间&#xff0c;并将开始日期设置为当前日期&#xff0c;将结束日期设置为当前日期加一年 三. 存款计算 1.从文本框获取当前资金、利率、定期期…

无人机高山景区物资吊运技术及前景分析

随着科技的飞速发展&#xff0c;无人机技术已经逐渐渗透到各个领域&#xff0c;并在其中展现出巨大的潜力和应用前景。在高山景区物资运输方面&#xff0c;无人机技术的引入不仅解决了传统运输方式中人力成本高、效率低下的问题&#xff0c;还极大地提升了运输的安全性和灵活性…

就是这个样的粗爆,手搓一个计算器:数线计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"><div class"input-group"><label for"a">…