513.找树左下角的值

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

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

示例 1:

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

示例 2:

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

提示:

二叉树的节点个数的范围是 [1,104]

-231 <= Node.val <= 231 - 1


正解

我们来分析一下题目:在树的最后一行找到最左边的值。

首先要是最后一行,然后是最左边的值。

如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。

所以要找深度最大的叶子节点。

那么如何找最左边的呢?可以使用前序遍历,保证优先左边搜索;

然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。

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

    参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。

    本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。
  2. 确定终止条件

    当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
  3. 确定单层递归的逻辑

    在找最大深度的时候,递归的过程中依然要使用回溯。
上代码(●'◡'●)
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) {
traversal(root->left, depth + 1); // 隐藏着回溯
}
if (root->right) {
traversal(root->right, depth + 1); // 隐藏着回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};

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
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

树中节点的数目在范围 [0, 5000] 内

-1000 <= Node.val <= 1000

-1000 <= targetSum <= 1000


正解

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

  1. 确定递归函数的参数和返回类型

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

    返回值:遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
  2. 确定终止条件

    首先计数器如何统计这一条路径的和呢?

    不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。

    如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。

    如果遍历到了叶子节点,count不为0,就是没找到。
  3. 确定单层递归的逻辑

    因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。

    递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
上代码(●'◡'●)
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
if (!root->left && !root->right && sum == root->val) {
return true;
}
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
};

可以看出,代码很精简,但隐藏了许多过程,包括回溯的过程;

如果都展开的话应该是这个样子:

class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!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);
}
};

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]

提示:

1 <= inorder.length <= 3000

postorder.length == inorder.length

-3000 <= inorder[i], postorder[i] <= 3000

inorder 和 postorder 都由 不同 的值组成

postorder 中每一个值都在 inorder 中

inorder 保证是树的中序遍历

postorder 保证是树的后序遍历


正解

首先回忆一下如何根据两个顺序构造一个唯一的二叉树:

以 后序数组的最后一个元素为切割点,先切中序数组;

根据中序数组,反过来再切后序数组。

一层一层切下去,每次后序数组最后一个元素就是节点元素。



说到一层一层切割,就应该想到了递归。

来看一下一共分几步:

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

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

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

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

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

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

此时应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭;

这个就是不变量,要在递归中保持这个不变量。

在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭右闭,必然乱套!

首先要切割中序数组,为什么先切割中序数组呢?

切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。

中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割

接下来就要切割后序数组了。

首先后序数组的最后一个元素指定不能要了,这是切割点 也是 当前二叉树中间节点的元素,已经用了。

后序数组的切割点怎么找?

后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。

此时有一个很重的点,就是中序数组大小一定是和后序数组的大小相同的(这是必然)

中序数组我们都切成了左中序数组和右中序数组了;

那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

此时,中序数组切成了左中序数组和右中序数组,后序数组切割成左后序数组和右后序数组。

接下来可以递归了。

上代码(●'◡'●)
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);
}
};

写博不易,请大佬点赞支持一下8~

代码随想录Day16的更多相关文章

  1. 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素

    第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...

  2. 代码随想录第八天 |344.反转字符串 、541. 反转字符串II、剑指Offer 05.替换空格 、151.翻转字符串里的单词 、剑指Offer58-II.左旋转字符串

    第一题344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这 ...

  3. 代码随想录-day1

    链表 今天主要是把链表专题刷完了,链表专题的题目不是很难,基本都是考察对链表的操作的理解. 在处理链表问题的时候,我们通常会引入一个哨兵节点(dummy),dummy节点指向原链表的头结点.这样,当我 ...

  4. 代码随想录 day0 博客怎么写

    前言 2.25日开始记录自己的博客生涯以及代码随想录训练营的每日内容 一.题目链接怎么找?怎么设置连接? 力扣题目链接1:力扣 二.正文怎么写? 二分查找 算法思路: 二分查找需要保证数组为有序数组同 ...

  5. 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)

    背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...

  6. 代码随想录算法训练营day16 | leetcode ● 104.二叉树的最大深度 559.n叉树的最大深度 ● 111.二叉树的最小深度 ● 222.完全二叉树的节点个数

    基础知识 二叉树的多种遍历方式,每种遍历方式各有其特点 LeetCode 104.二叉树的最大深度 分析1.0 往下遍历深度++,往上回溯深度-- class Solution { int deep ...

  7. 代码随想录第七天| 454.四数相加II、383. 赎金信 、15. 三数之和 、18. 四数之和

    第一题454.四数相加II 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= i, ...

  8. 代码随想录算法训练营day22 | leetcode 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点

    LeetCode 235. 二叉搜索树的最近公共祖先 分析1.0  二叉搜索树根节点元素值大小介于子树之间,所以只要找到第一个介于他俩之间的节点就行 class Solution { public T ...

  9. 代码随想录算法训练营day17 | leetcode ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

    LeetCode 110.平衡二叉树 分析1.0 求左子树高度和右子树高度,若高度差>1,则返回false,所以我递归了两遍 class Solution { public boolean is ...

  10. 代码随想录算法训练营day13

    基础知识 二叉树基础知识 二叉树多考察完全二叉树.满二叉树,可以分为链式存储和数组存储,父子兄弟访问方式也有所不同,遍历也分为了前中后序遍历和层次遍历 Java定义 public class Tree ...

随机推荐

  1. 状态模式(Sate Pattern)

    一.模式动机 状态模式(State Pattern)是一种较为复杂的行为型模式.它用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题.当系统中某个对象存在多个状态,这些状态之间可以进行转换, ...

  2. [代码]C语言进行md5,SHA256,SHA512加密

    前言 原本在学puppet,它的user资源需要设置hash后的散列值,结果-我把加密算法,shadow文件,密码破解搞了个遍- 环境 CentOS7 gcc编译器 /etc/shadow文件解析 文 ...

  3. W801单片机入门开发环境设置

    W801单片机入门开发环境设置 开发软件下载 烧录工具和SDK 在 WinnerMicro的网站 https://www.winnermicro.com/html/1/156/158/558.html ...

  4. python重拾基础第四天

    本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1. 列表生成式,迭代器&生成器 列表生成式 我现在有个需求, ...

  5. ​Chrome插件:Postman Interceptor 调试的终极利器

    今天给大家介绍一款非常实用的工具--Postman Interceptor. 这个工具可以捕捉任何网站的请求,并将其发送到Postman客户端. 对于经常和API打交道的程序员来说,Postman I ...

  6. LaTeX 编辑协作平台 Overleaf 安装和使用教程

    在学术界和科技行业,LaTeX 已成为撰写高质量文档的标准工具.然而,传统的 LaTeX 使用体验常常伴随着以下挑战: 学习曲线陡峭 环境配置复杂 多人协作困难 实时预览不便 当然,市面上不乏很多在线 ...

  7. Python潮流周刊的优惠券和精美电子书(EPUB、PDF、Markdown)

    Python潮流周刊从 2023.05.13 连载至今,本周即将发布第 60 期,这意味着我们又要达成一个小小的里程碑啦! 每周坚持做分享,周复一周,这对自己的精力和意志是一项不小的挑战.于是,为了让 ...

  8. PowerBuilder编程新思维6.5:外传1(PowerPlume的设计与规划)

    <第五部分 Otherside 意外的宝藏> 每一颗种子都有发芽的梦想.PowerPlume(孔雀翎)开发交流群:286502392    PowerBuilder编程新思维6.5:外传1 ...

  9. webgl(threejs)生成房间楼层

    楔子 在很多数字孪生项目中,都会涉及到楼层的建模.楼层的建模由于结构繁多,如果都是建模师进行手动建模,工作量会比较大.而楼层本身的结构,可以抽象成可以通过路径构造的对象(这和之前的文章提及的的管路以及 ...

  10. LeetCode102.二叉树的层序遍历

    LeetCode题目链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/submissions/548489149/ 题 ...