代码随想录第十六天 | Leecode 513. 找树左下角的值、112. 路径总和、113. 路径总和 II、106. 从中序与后序遍历序列构造二叉树
Leecode 513. 找树左下角的值
题目描述
给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
- 示例 1:
输入:root = [2,1,3]
输出:1
- 示例 2:
输入:[1,2,3,4,null,5,6,null,null,7]
输出:7
迭代法
本题要求最后一层的最左侧节点,那么非常适合采用层序遍历的范式,到最后一层的第一个节点就是要找的数。但由于从上往下层序遍历的时候,并不知道当前层是否是最后一层,因此我们考虑使用一个vector来记录每一层的第一个节点。在遍历结束之后,再从这个vector中取出最后一个数,即为我们要找的最后一层的第一个节点。因此可以有代码如下:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
if(!root) return 0;
queue<TreeNode*> nodeQue;
nodeQue.push(root);
vector<int> firstVal; // 用于存放每一层最左侧的值
while(!nodeQue.empty()){
int size = nodeQue.size();
firstVal.push_back(nodeQue.front()->val); // 将当前层第一个节点的值放入firstVal中
for(int i = 0; i < size; i++){
TreeNode* cur = nodeQue.front();
nodeQue.pop();
if(cur->left) nodeQue.push(cur->left);
if(cur->right) nodeQue.push(cur->right);
}
}
return firstVal[firstVal.size()-1]; // 最后返回最后一层的第一个节点的值
}
};
对于层序遍历的标准模板应该都很熟悉了,本题的时间复杂度同样也是\(O(n)\)(即每个节点遍历一次),空间复杂度为\(O(\log n)\)(额外的一个vector大小与二叉树深度相等)。
Leecode 112. 路径总和
题目描述
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
- 示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1]
,targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
- 示例 2:
输入:root = [1,2,3]
,targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2)
: 和为3
(1 --> 3)
: 和为4
不存在 sum = 5 的根节点到叶子节点的路径。
- 示例 3:
输入:
root = []
,targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。
回溯法递归
本题和之前那道求所有路径的题目非常相似,同样也是只要将到达每个节点所经历的路径都记录下来,如果走到叶节点,则将当前路径和进行存放,最后再遍历检查一遍存放的路径和中是否存在有等于目标值的情况,如果有则返回true,否则返回false。根据这个思路可以得到如下代码:
class Solution {
public:
void pathSumHelper(TreeNode* curNode, vector<int>& pathSum, vector<TreeNode*> ndVec){ // 参数表示:当前节点、存放所有路径和的vec、存放到达当前节点的路径
if(!curNode) return; // 如果当前节点为空,则直接返回
ndVec.push_back(curNode); // 否则将当前节点纳入路径中
if(!curNode->left && !curNode->right){ // 如果当前节点为叶节点,则计算路径和,并存放
int curSum = 0; // 初始化路径和
for(int i = 0; i < ndVec.size(); i++){ // for循环遍历路径,求路径和
curSum += ndVec[i]->val;
}
pathSum.push_back(curSum); // 将路径和进行存放
}
if(curNode->left) pathSumHelper(curNode->left, pathSum, ndVec); // 递归回溯左子节点,回溯体现在其中的值传递
if(curNode->right) pathSumHelper(curNode->right, pathSum, ndVec); // 递归回溯右子节点
return;
}
bool hasPathSum(TreeNode* root, int targetSum) {
vector<int> pathSum; // 用于存放所有路径和的vec
vector<TreeNode*> ndVec; // 用于存放当前路径中经过的所有节点
pathSumHelper(root, pathSum, ndVec); // 递归调用赋值函数,将所有路径和存放在pathSum中
for(int i = 0; i < pathSum.size(); i++){ // 遍历pathSum,检查是否有和目标值相等的值
if(targetSum == pathSum[i]) return true; //
}
return false;
}
};
同样也可以直接传入一个布尔变量和原本的目标值,在计算完路径和后直接进行比较并修改布尔遍历。这样不必使用一个vector来存放路径和,一定程度上降低空间复杂度:
class Solution {
public:
void pathSumHelper(TreeNode* curNode, vector<TreeNode*> ndVec, bool& result, int targetSum){ // 传入布尔变量和目标值
if(result || !curNode) return; // 如果已经找到,或者当前节点为空,都要返回
ndVec.push_back(curNode);
if(!curNode->left && !curNode->right){
int curSum = 0;
for(int i = 0; i < ndVec.size(); i++){
curSum += ndVec[i]->val;
}
if(curSum == targetSum){ // 计算完路径和后直接与目标值比较,如果相等的话就将result变为true并返回
result = true;
return;
}
}
if(curNode->left) pathSumHelper(curNode->left, ndVec, result, targetSum);
if(curNode->right) pathSumHelper(curNode->right, ndVec, result, targetSum);
return;
}
bool hasPathSum(TreeNode* root, int targetSum) {
vector<int> pathSum;
vector<TreeNode*> ndVec;
bool result = false; // 初始没有找到,设置为false
pathSumHelper(root, ndVec, result, targetSum); // 递归调用
return result;
}
};
Leecode 113. 路径总和 II
题目描述
给你二叉树的根节点 root 和一个整数目标和
targetSum` ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
- 示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1]
,targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
- 示例 2:
输入:root = [1,2,3]
,targetSum = 5
输出:[]
- 示例 3:
输入:
root = [1,2]
,targetSum = 0
输出:[]
回溯递归
和上一题非常类似的方法,区别仅在于需要用一个vector来存放路径。
class Solution {
public:
void pathSumHelper(TreeNode* curNode, vector<vector<int>>& result, vector<TreeNode*> ndVec, int targetSum){
if(!curNode) return;
ndVec.push_back(curNode);
if(!curNode->left && !curNode->right) {
vector<int> path;
int sum = 0;
for(int i = 0; i < ndVec.size(); i++){
sum += ndVec[i]->val;
path.push_back(ndVec[i]->val);
}
if(sum == targetSum)result.push_back(path);
}
if(curNode->left) pathSumHelper(curNode->left, result, ndVec, targetSum);
if(curNode->right) pathSumHelper(curNode->right, result, ndVec, targetSum);
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> result;
vector<TreeNode*> ndVec;
pathSumHelper(root, result, ndVec, targetSum);
return result;
}
};
Leecode 106. 从中序与后序遍历序列构造二叉树
题目描述
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
- 示例 1:
输入:inorder = [9,3,15,20,7]
,postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
- 示例 2:
输入:
inorder = [-1]
,postorder = [-1]
输出:[-1]
解题思路与代码展示
本题从思路上不难理解,后序遍历的最后一个数表示了当前树中的根节点,根据根节点中的值将前序序列分成两半,可以得到左右两个子树的前序序列。同时根据分割后的前序序列长度也可以将后序序列以同样的长度进行分割,得到左右子树的后序序列。从而再进行递归,即可得到整颗树的结构。故我们可以写出以下代码:
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.empty() || postorder.empty()) return nullptr; // 处理递归终止条件,如果当前序列为空,则返回空
int rootVal = postorder[postorder.size()-1]; // 后序序列的最后一个值即为根节点中的值
TreeNode* root = new TreeNode(rootVal); // 新建根节点
int cutPoint = find(inorder.begin(), inorder.end(), rootVal) - inorder.begin(); // 查找根节点在前序序列中的位置
vector<int> leftInorder(inorder.begin(), inorder.begin()+cutPoint); // 构造左子树的前序序列
vector<int> leftPostorder(postorder.begin(), postorder.begin()+cutPoint); // 构造左子树的后序序列
vector<int> rightInorder(inorder.begin() + cutPoint + 1, inorder.end()); // 构造右子树的前序序列
vector<int> rightPostorder(postorder.begin() + cutPoint, postorder.end()-1); // 构造右子树的后续序列
root->left = buildTree(leftInorder, leftPostorder); // 对左子树进行递归
root->right = buildTree(rightInorder, rightPostorder); // 对右子树进行递归
return root;
}
};
上面代码对两个vector进行递归即可建立整个二叉树。同时这部分有几道非常类似的题目,都是根据遍历结果构建二叉树,下面只贴出链接和相应代码,并不过多赘述。
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty() || inorder.empty()) return nullptr;
TreeNode* root = new TreeNode(preorder[0]);
int cutPoint = find(inorder.begin(), inorder.end(), root->val) - inorder.begin();
vector<int> leftPre(preorder.begin()+1, preorder.begin()+cutPoint+1);
vector<int> leftIn(inorder.begin(), inorder.begin()+cutPoint);
vector<int> rightPre(preorder.begin()+cutPoint+1, preorder.end());
vector<int> rightIn(inorder.begin()+cutPoint+1, inorder.end());
root->left = buildTree(leftPre, leftIn);
root->right = buildTree(rightPre, rightIn);
return root;
}
};
class Solution {
public:
TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
if (preorder.empty()) return nullptr;
TreeNode* root = new TreeNode(preorder[0]);
if (preorder.size() == 1) return root;
int L = 0;
// 查找前序第二个元素(左子树根)在后序中的位置
for (int i = 0; i < postorder.size(); ++i) {
if (postorder[i] == preorder[1]) {
L = i + 1; // 左子树节点数 = 位置索引 + 1
break;
}
}
// 分割左子树数组
vector<int> leftPre(preorder.begin() + 1, preorder.begin() + 1 + L);
vector<int> leftPost(postorder.begin(), postorder.begin() + L);
// 分割右子树数组(修正结束位置)
vector<int> rightPre(preorder.begin() + 1 + L, preorder.end());
vector<int> rightPost(postorder.begin() + L, postorder.end() - 1);
// 递归构建
root->left = constructFromPrePost(leftPre, leftPost);
root->right = constructFromPrePost(rightPre, rightPost);
return root;
}
};
今日总结
今天题目都还挺简单,最难的其实是最后刷了一道用前序+后序建立二叉树,在没有中序的情况下可能有多个解,但也需要能够想到怎么才能分割出满足条件的解,
今天力扣到60题了,再接再厉!
代码随想录第十六天 | Leecode 513. 找树左下角的值、112. 路径总和、113. 路径总和 II、106. 从中序与后序遍历序列构造二叉树的更多相关文章
- 代码随想录算法训练营day18 | leetcode 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树
LeetCode 513.找树左下角的值 分析1.0 二叉树的 最底层 最左边 节点的值,层序遍历获取最后一层首个节点值,记录每一层的首个节点,当没有下一层时,返回这个节点 class Solutio ...
- Leetcode之深度优先搜索(DFS)专题-513. 找树左下角的值(Find Bottom Left Tree Value)
Leetcode之深度优先搜索(DFS)专题-513. 找树左下角的值(Find Bottom Left Tree Value) 深度优先搜索的解题详细介绍,点击 给定一个二叉树,在树的最后一行找到最 ...
- LeetCode 513. 找树左下角的值(Find Bottom Left Tree Value)
513. 找树左下角的值 513. Find Bottom Left Tree Value 题目描述 给定一个二叉树,在树的最后一行找到最左边的值. LeetCode513. Find Bottom ...
- Java实现 LeetCode 513 找树左下角的值
513. 找树左下角的值 给定一个二叉树,在树的最后一行找到最左边的值. 示例 1: 输入: 2 / \ 1 3 输出: 1 示例 2: 输入: 1 / \ 2 3 / / \ 4 5 6 / 7 输 ...
- 513 Find Bottom Left Tree Value 找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值. 详见:https://leetcode.com/problems/find-bottom-left-tree-value/description/ C+ ...
- 领扣(LeetCode)找树左下角的值 个人题解
给定一个二叉树,在树的最后一行找到最左边的值. 示例 1: 输入: 2 / \ 1 3 输出: 1 示例 2: 输入: 1 / \ 2 3 / / \ 4 5 6 / 7 输出: 7 注意: 您可以假 ...
- [Swift]LeetCode513. 找树左下角的值 | Find Bottom Left Tree Value
Given a binary tree, find the leftmost value in the last row of the tree. Example 1: Input: 2 / \ 1 ...
- Leetcode513. Find Bottom Left Tree Value找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值. 示例 1: 输入: 2 / \ 1 3 输出: 1 示例 2: 输入: 1 / \ 2 3 / / \ 4 5 6 / 7 输出: 7 注意: 您可以假 ...
- javaSE第十六天
第十六天 140 1:List的子类(掌握) 140 (1)List的子类特点 140 (2)ArrayList 141 A:没有特有功能需要学习 141 B:案例 ...
- HihoCoder第十周:后序遍历
也就在大二学数据结构的时候知道了树的前序遍历.后序遍历.中序遍历.之后就忘了,在之后就是大四研究生老师考我,我当时还不知道,真够丢人的.自此之后,知道了如何通过其中两个得到第三个,但是也没有编程实现过 ...
随机推荐
- Flink同步kafka到iceberg(cos存储)
一.flink到logger 1.source create table source_table ( id bigint comment '唯一编号' ,order_number bigint co ...
- Luogu P4425 转盘 题解 [ 黑 ] [ 线段树 ] [ 贪心 ] [ 递归 ]
转盘:蒟蒻的第一道黑,这题是贪心和线段树递归合并的综合题. 贪心 破环成链的 trick 自然不用多说. 首先观察题目,很容易发现一个性质:只走一圈的方案一定最优.这个很容易证,因为再绕一圈回来标记前 ...
- 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
在人工智能飞速发展的今天,大语言模型的应用越来越广泛.DeepSeek 作为近期爆火的一款大语言模型,受到了众多开发者的青睐. 今天这篇内容,就来聊聊,如何在本地自己的电脑上部署DeepSeek. 1 ...
- java推送企业微信消息
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcl ...
- Postman 调试 SignalR 发布的wss接口
微软官网的SignalR例子拿来跑即可 Postman 调试SignalR 发送post请求获取token 将获取到的connectionToken加入ws链接 点击 Connect 发送第一条协商消 ...
- MySQL - 数据更新场景
Excel文件数据更新到表A的某两个字段 Excel文件中Sheet1页有两列,一列是序号,另一列是手机号.表A中有对应的序号列和手机号列. 1.首先,使用Navicat将Excel数据导入数据库,注 ...
- 容器引擎-Docker
Docker是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级.可移植的.自给自足的容器.Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱和集装箱之间没有影响. ...
- harbor
一篇带你了解私有仓库 Harbor 的搭建 一.Harbor简介 虽然Docker官方提供了公共的镜像仓库,但是从安全和效率等方面考虑,部署我们私有环境内的Registry也是非常必要的. Harbo ...
- Docker 容器跨主机多网段通信解决方案
一.MacVlan实现Docker的跨主机网络通信的方案有很多,如之前博文中写到的通过部署 Consul服务实现Docker容器跨主机通信 Macvlan工作原理: Macvlan是Lin ...
- 用于线程同步的Interlocked系列函数主要有哪些
原子访问 通过Interlocked系列函数是 Windows API 提供的一组原子操作函数,用于在多线程环境中安全地操作共享变量.当我们执行这些Interlocked系列函数的时候 ,函数会对总线 ...