513.找树左下角的值
题目链接:513.找树左下角的值
文档讲解:代码随想录/找树左下角的值
视频讲解:视频讲解-找树左下角的值
状态:已完成(2遍)
解题过程
看到题目的第一想法
这道题终于让我时隔已久的逮到用层序遍历的机会了,直接一层层找下去,最后一层的第一个元素就是树左下角的值,十分简便。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var findBottomLeftValue = function(root) {
let queue = [],ans = [];
queue = [root];
while(queue.length){
let len = queue.length;
let smallAns = [];
while(len--){
let node = queue.shift();
smallAns.push(node.val);
node.left&&queue.push(node.left);
node.right&&queue.push(node.right);
}
ans.push(smallAns);
}
return ans[ans.length-1][0];
};
运行提交都没有问题。
看完代码随想录之后的想法
这题用递归法的话还是和深度联系起来,找到深度最大的第一个叶子结点,即可完成题目要求。
看了讲解手搓代码如下:
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var findBottomLeftValue = function(root) {
let maxPath = 0,ans = 0;
const leftFirst = function(node,path){
//终止条件
if(node.left == null&&node.right == null){
if(path>maxPath){
//确保只记录每次深度最大的时候的第一个节点
maxPath=path;
ans=node.val;
}
}
node.left&&leftFirst(node.left,path+1);
node.right&&leftFirst(node.right,path+1);
}
leftFirst(root,1);
return ans;
};
总结
递归虽然巧妙,但有时不那么容易想到,所以层序迭代也是十分重要的。
二刷还是没抵挡住层序遍历的诱惑哈哈。
112. 路径总和
题目链接:112. 路径总和
文档讲解:代码随想录/路径总和
视频讲解:视频讲解-路径总和
状态:已完成(2遍)
解题过程
看到题目的第一想法
这题我第一想法是递归,既然要计算每条路径之和,那么也就意味着要把父节点的值带到子节点里去运算,那么肯定选择前序遍历。每当遍历到叶子结点的时候,将当前路径的和与目标值作比对,如果相同,则将ans改为true,否则不用做改动。
手搓代码如下:
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} targetSum
* @return {boolean}
*/
var hasPathSum = function (root, targetSum) {
let ans = false;
const isSum = function (node, sum) {
if(node == null)return;
if (node.left == null && node.right == null) {//等到叶子结点的时候再把路径和和目标值作比对
if (sum+node.val == targetSum) {
ans = true;
}
}
node.left && isSum(node.left, sum + node.val);
node.right && isSum(node.right, sum + node.val);
}
isSum(root,0)
return ans;
};
提交没有问题,能直接用递归做出来还是很高兴的。
看完代码随想录之后的想法
这里要纠正一下刚刚的思路,因为这里对左右子节点的递归,是直接在参数里加当前节点的val的,所以其实用什么序递归都没问题。讲解代码是用了目标值减当前数的方法,理解起来似乎略有困难。
讲解代码如下:
/**
* @param {treenode} root
* @param {number} targetsum
* @return {boolean}
*/
let haspathsum = function (root, targetsum) {
// 递归法
const traversal = (node, cnt) => {
// 遇到叶子节点,并且计数为0
if (cnt === 0 && !node.left && !node.right) return true;
// 遇到叶子节点而没有找到合适的边(计数不为0),直接返回
if (!node.left && !node.right) return false;
// 左(空节点不遍历).遇到叶子节点返回true,则直接返回true
if (node.left && traversal(node.left, cnt - node.left.val)) return true;
// 右(空节点不遍历)
if (node.right && traversal(node.right, cnt - node.right.val)) return true;
return false;
};
if (!root) return false;
return traversal(root, targetsum - root.val);
// 精简代码:
// if (!root) return false;
// if (!root.left && !root.right && targetsum === root.val) return true;
// return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
};
总结
这道题因为单层递归逻辑中针对中间节点是没有处理逻辑的,所以前中后序递归都是可以的。
二刷对自己的递归方法做了一点修改,感觉第一遍做的时候在最后叶子结点没有return。
var hasPathSum = function (root, targetSum) {
let ans = false;
const isSum = function (node, sum) {
if(node == null)return;
if (node.left == null && node.right == null) {//等到叶子结点的时候再把路径和和目标值作比对
if (sum+node.val == targetSum) {
ans = true;
}
return;
}
node.left && isSum(node.left, sum + node.val);
node.right && isSum(node.right, sum + node.val);
}
isSum(root,0)
return ans;
};
106.从中序与后序遍历序列构造二叉树
题目链接:106.从中序与后序遍历序列构造二叉树
视频讲解:视频讲解-从中序与后序遍历序列构造二叉树
状态:已完成(2遍)
解题过程
看到题目的第一想法
直接投降。
看完代码随想录之后的想法
确实是第一次见识到这个思路,每次先看后序,找到中间节点,再去中序里切割为左中右,然后再去后序里对应切割,再找到每个部分的中间节点,如此往复。
讲解代码如下:
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
if (!inorder.length) return null;
const rootVal = postorder.pop(); // 从后序遍历的数组中获取中间节点的值, 即数组最后一个值
let rootIndex = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
const root = new TreeNode(rootVal); // 创建中间节点
root.left = buildTree(inorder.slice(0, rootIndex), postorder.slice(0, rootIndex)); // 创建左节点
root.right = buildTree(inorder.slice(rootIndex + 1), postorder.slice(rootIndex)); // 创建右节点
return root;
};
希望二刷的时候可以对这个思路以及切割方法印象深刻。
总结
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间