Day18
找树左下角的值
https://leetcode.cn/problems/find-bottom-left-tree-value/
题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-upXr09Dl-1688314977613)(https://note.youdao.com/yws/res/89225/WEBRESOURCEe047eccc0eefeef9494e94e6bfab78d8)]
思路
本题要找出树的最后一行的最左边的值。此时大家应该想起用层序遍历是非常简单的了,反而用递归的话会比较难一点。
代码
递归(回溯)
递归三部曲:
1、确定递归函数的参数和返回值
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。
本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。
int maxDeph = -1;
int res = 0;
2、确定终止条件
当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
if(root.left == null && root.right == null){
if(depth > maxDeph){
res = root.val;
maxDeph = depth;
}
}
3、确定单层递归的逻辑(前序遍历)
// 中
// 左
if(root.left != null){
depth++;
dfs(root.left, depth);
depth--;
}
// 右
if(root.right != null){
depth++;
dfs(root.right, depth);
depth--;
}
class Solution {
int maxDeph = -1;
int res = 0;
public int findBottomLeftValue(TreeNode root) {
dfs(root, 0);
return res;
}
public void dfs(TreeNode root, int depth){
if(root.left == null && root.right == null){
if(depth > maxDeph){
res = root.val;
maxDeph = depth;
}
}
if(root.left != null){
depth++;
dfs(root.left, depth);
depth--;
}
if(root.right != null){
depth++;
dfs(root.right, depth);
depth--;
}
}
}
迭代
class Solution {
public int findBottomLeftValue(TreeNode root) {
if (root == null) return 0;
int res = 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
for(int i = 0; i < size; i++){
TreeNode node = q.poll();
if(i == 0) res = node.val;
if(node.left != null) q.offer(node.left);
if(node.right != null) q.offer(node.right);
}
}
return res;
}
}
路径总和
https://leetcode.cn/problems/path-sum/
题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt2EU3Ve-1688314977614)(https://note.youdao.com/yws/res/89245/WEBRESOURCE37d4e5c12f5a3b1c496bf5975babe16f)]
思路
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
代码
递归(回溯)
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
return dfs(root, targetSum - root.val);
}
// 确定返回值
private boolean dfs(TreeNode root, int count){
// 确定递归结束条件
if(root.left == null && root.right == null && count == 0){
return true;
}
if(root.left == null && root.right == null){
return false;
}
// 本层逻辑(回溯)
if(root.left != null){
count -= root.left.val;
if(dfs(root.left, count)) return true;
count += root.left.val;
}
// 本层逻辑(回溯)
if(root.right != null){
count -= root.right.val;
if(dfs(root.right, count)) return true;
count += root.right.val;
}
return false;
}
}
路径总和ii
https://leetcode.cn/problems/path-sum-ii/
题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sqX3dV6K-1688314977614)(https://note.youdao.com/yws/res/89253/WEBRESOURCE8bf680ee36cb3540dc615786cbe96209)]
思路
要遍历整个树,找到所有路径,所以递归函数不要返回值!
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> paths = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if(root == null) return res;
paths.add(root.val);
traversal(root, targetSum - root.val);
return res;
}
public void traversal(TreeNode root, int count){
if(root.left == null && root.right == null && count == 0){
res.add(new ArrayList(paths)); // 这里要重新创建一个列表
return;
}
if(root.left == null && root.right == null){
return;
}
if(root.left != null){
count -= root.left.val;
paths.add(root.left.val);
traversal(root.left, count);
paths.remove(paths.size() - 1); // 回溯
count += root.left.val; // 回溯
}
if(root.right != null){
count -= root.right.val;
paths.add(root.right.val);
traversal(root.right, count);
paths.remove(paths.size() - 1);
count += root.right.val;
}
}
}
从中序与后序遍历序列构造二叉树
https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8glADu1o-1688314977614)(https://note.youdao.com/yws/res/89260/WEBRESOURCE80c1ab36cc1a73e177e858473124140f)]
思路
那么代码应该怎么写呢?
说到一层一层切割,就应该想到了递归。
来看一下一共分几步:
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
代码
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder.length == 0 || inorder.length == 0)
return null;
return traversal(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
public TreeNode traversal(int[] inorder, int inorderLeft, int inorderRight, int[] postorder, int postorderLeft, int postorderRight){
// 1、如果数组大小为零的话,说明是空节点了。
if(postorderLeft == postorderRight) return null;
// 2、如果不为空,那么取后序数组最后一个元素作为节点元素。
int last = postorder[postorderRight - 1];
TreeNode root = new TreeNode(last);
// 3、找切割点
int mid;
for(mid = inorderLeft; mid < inorderRight; mid++){
if (inorder[mid] == last) break;
}
// 4、切割中序数组,得到 中序左数组和中序右数组
int leftInorderStart = inorderLeft;
int leftInorderEnd = mid;
int rightInorderStart = mid + 1;
int rightInorderEnd = inorderRight;
// 5、切割后序数组,得到 后序左数组和后序右数组
int leftPostorderStart = postorderLeft;
int leftPostorderEnd = postorderLeft + (mid - inorderLeft);
int rightPostorderStart = leftPostorderEnd;
int rightPostorderEnd = postorderRight - 1;
// 6、递归处理左区间和右区间
root.left = traversal(inorder, leftInorderStart, leftInorderEnd, postorder, leftPostorderStart, leftPostorderEnd);
root.right = traversal(inorder, rightInorderStart, rightInorderEnd, postorder, rightPostorderStart, rightPostorderEnd);
return root;
}
}