Leetcode 513.找树左下角的值
题目:513.找树左下角的值
初始思路
代码
var findBottomLeftValue = function (root) {if (!root) return nulllet queue = []queue.push(root)let reswhile (queue.length) {let length = queue.lengthfor (let i = 0; i < length; i++){let node = queue.shift()if (i === 0) {res = node.val}if (node.left) {queue.push(node.left)}if (node.right) {queue.push(node.right)}}}return res};
var findBottomLeftValue = function (root) {let maxPath = 0, resNode = nullconst dfsTree = function (node, curPath) {if (node.left == null && node.right == null) {if (curPath > maxPath) {// 更新最大深度maxPath = curPath// 最大深度最左边的数值resNode = node.val}}// 单层递归逻辑if (node.left) {dfsTree(node.left, curPath + 1)}if (node.right) {dfsTree(node.right, curPath + 1)}}dfsTree(root, 1)return resNode}
感想
- 这题层序遍历模板比递归简单。递归需要考虑如何找到最下面一层。然后再找到最左边的节点。
Leetcode 112.路径总和
初始思路
之前有道题是求所有路径,对所有路径再求和的话也可以写出来,但是可能会多遍历一些不必要的路径。
代码
var hasPathSum = function (root, targetSum) {const traversal = function (node, cnt) {// 结束条件if (cnt === 0 && node.left == null && node.right== null) {// 如果遇到叶子节点的时候并且计数为0,说明正好是我们需要的路径return true}if (cnt !== 0 && node.left == null && node.right == null) {// 遇到了叶子节点,但是计数不为0,直接返回return false}// 左(空节点不遍历).遇到叶子节点返回true,则直接返回trueif (node.left != null && traversal(node.left, cnt - node.left.val)) {return true}// 右(空节点不遍历)if (node.right != null && traversal(node.right, cnt - node.right.val)) {return true}return false}if (!root) return false// 把目标值传进去,每经过一个节点就减去这个节点的值,如果最后是0的话说明是我们要的路径。不采用累加的做法return traversal(root, targetSum - root.val)};
// 迭代var hasPathSum = function (root, targetSum) {if (!root) return falselet nodeArr = [root]let valArr = [0]while (nodeArr.length) {let curNode = nodeArr.shift()let curVal = valArr.shift()curVal += curNode.val// 为叶子结点,且和等于目标数,返回trueif (curVal === targetSum && curNode.left == null && curNode.right == null) {return true}// 左if (curNode.left) {// 放入下一个左节点nodeArr.push(curNode.left)// 放入当前路径的和valArr.push(curVal)}// 右if (curNode.right) {// 放入下一个左节点nodeArr.push(curNode.right)// 放入当前路径的和valArr.push(curVal)}}return false}
感想
- 递归的做法更容易理解。采用传入目标值,然后经过路径的时候相减。最后等于0的时候说明是要找的路径
- 迭代法利用的是求和累加,每路过一个节点就加上这个节点的值。
Leetcode 113.路径总和 II
题目:113.路径总和 II
初始思路
相比上一题直接返回布尔值,这题需要返回满足条件的路径。需要同时记录下路径。
代码
var pathSum = function (root, targetSum) {const res = []const traversal = function (node, cnt, path) {// 判断路径if (cnt === 0 && node.left == null && node.right == null) {// 因为path是一个数组,所以需要解构赋值之后把值放进去res.push([...path])return}if (cnt !== 0 && node.left == null && node.right == null) {// 遇到了叶子节点,但是计数不为0return}// 左 (空节点不遍历)if (node.left) {path.push(node.left.val)// 递归traversal(node.left, cnt - node.left.val, path)// 回溯path.pop()}// 右 (空节点不遍历)if (node.right) {path.push(node.right.val)// 递归traversal(node.right, cnt - node.right.val, path)// 回溯path.pop()}return}if (!root) return res// 把根节点的值减掉当做初始值。把根节点放入路径中traversal(root, targetSum - root.val, [root.val])return res};
// 迭代var pathSum = function (root, targetSum) {if (!root) return []let nodeArr = [root]let resArr = [] // 记录符合目标和的返回路径let tempArr = [[]] // 对应路径let countArr = [0] //对应和while (nodeArr.length) {let curNode = nodeArr.shift()let curVal = countArr.shift()let path = tempArr.shift()curVal += curNode.valpath.push(curNode.val)// 为叶子结点,且和等于目标数,将此次结果数组push进返回数组中if (curVal === targetSum && curNode.left == null && node.right == null) {resArr.push(path)}// 左,将当前的和及对应路径也对应记录下来if (curNode.left) {nodeArr.push(curNode.left)countArr.push(curVal)tempArr.push([...path])}// 右,将当前的和及对应路径也对应记录下来if (curNode.right) {nodeArr.push(curNode.right)countArr.push(curVal)tempArr.push([...path])}}return resArr}
感想
- 当在遍历的时候走到叶子节点的时候需要回溯,回到父节点。回溯有些没理解。
- 图示:


Leetcode 106.从中序与后序遍历序列构造二叉树
题目:106.从中序与后序遍历序列构造二叉树 讲解:https://www.bilibili.com/video/BV1vW4y1i7dn
初始思路
数据结构经典问题,根据中序和后序构造二叉树。
因为后序是左右中,所以可以从最后一个节点获得根节点,然后再中序中找到这个根节点,确定左右节点分别是哪些。
代码
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};
感想
- 思路简单但是代码不好写。
- 学会切割!
Leetcode 105.从前序与中序遍历序列构造二叉树
初始思路
和上一题一样的思路,只不过前序的顺序是中左右,所以中节点出现在第一个元素,而后序遍历的中节点出现在最后一个元素。
代码
var buildTree = function (preorder, inorder) {if (!preorder.length) return null// 从后序遍历的数组中获取中间节点的值, 即数组第一个值。const rootVal = preorder.shift()// 获取中间节点在中序中的下标let rootIndex = inorder.indexOf(rootVal)// 创建中间节点const root = new TreeNode(rootVal)// 构造左子树root.left = buildTree(preorder.slice(0, rootIndex), inorder.slice(0, rootIndex))// 构造右子树// 注意在中序数组切割中避开中间的元素root.right = buildTree(preorder.slice(rootIndex), inorder.slice(rootIndex + 1))return root};
感想
- pop和shift方法都会操作原数组,此时数组已经改变了。
- 注意切割的方法,slice(start,end)不包括end,所以是左闭右开。
- 如果给前序和后序是不能构造出二叉树的,必须要有中序来确定左子树和右子树。
