104. Maximum Depth of Binary Tree
思路:顺着树的一个分支一直数层数直到叶子节点。DFS的思路。这个题目可以练习的是递归转迭代。
代码
695. Max Area of Island
思路:遇到点i,j;如果grid[i][j]没有被访问过,并且等于1,那么就数数i,j的4个方向上,能有多少个1。先数某一个方向,直到不能数下去;再换一个方向。
这道题目可以练习的是:判断是否访问过,可以开辟一个boolean数组;或者将访问过的grid[i][j] = 0;还可以练习递归转迭代。
代码
690. Employee Importance
思路:这个题目也没什么好说的。深度优先搜索=暴力搜索吧。学习递归改迭代。
代码
110. Balanced Binary Tree
思路:要检查每个节点的左右子树的高度。看高度相差是否大于1。一边计算树的高度,一边判断是否平衡,想了好久,代码没写出来。
代码
111. Minimum Depth of Binary Tree
思路:与112. Path Sum 一样,要注意条件是到叶子节点。
代码
513. Find Bottom Left Tree Value
思路:题目理解错误 find the leftmost value in the last row of the tree。找到树的最后一行最左边的值;我找到的是左子树深度最深的值。从左节点开始遍历。遇到深度更深的节点替换原来记录的leftVal。
学习:也可以用BFS 广度优先搜索来做。只是记住先遍历右子树。
513. Find Bottom Left Tree Value
思路:要在树的每一层找到最大值。可以用DFS或者BFS。BFS是优先考虑到的。要用DFS,就需要在遍历的时候记录当前访问的深度。同深度的数值最比较。需要找到每层数值的特征,肯定用BFS可以解决。
代码
529. Minesweeper
思路:题目不难,DFS。问题是写对dx和dy。
代码
547. Friend Circles
思路:DFS应用了一些业务逻辑,没有顺利完成。访问到一个点i,j等于1,接着检查位置(i,j+1),(i,j+2)….(j,0),(j,1)…(j,n),然后继续扩展。和Minesweeper挺像的。只是这种逻辑需要自己总结。
学习:还可以用并查集find-union的思路来解决。
代码
756. Pyramid Transition Matrix
思路:DFS。首先根据allowed构建前缀map。其次,根据bottom初始化pyramid底层。最后用dfs构建金字塔的每一层。例如bottom=”XXYX”,构建长度为3的层,长度为2的层,长度为1的层。思路虽简单,写代码的时候,要考虑当长度为len的构建完成,怎么转下一层。
学习:记录当前选择的字符有几种处理方式:可以用字符串保存,可以用一个list保存,也可以保存到二进制中。用二进制更快。二进制版本的详细解决看代码。
代码
337. House Robber III
思路:这道题目是一道典型的从暴力搜索向动态规划迈进的题目。
先说暴力搜索(其实就是DFS)。对于TreeNode node来讲。可以抢劫node,也可以不抢劫。最后返回数值大的。
public int rob(TreeNode root) {return dfs(root, true);}private int dfs(TreeNode node, boolean robAble) {if (node == null) {return 0;}if (node.left == null && node.right == null) {return robAble ? node.val : 0;}// 不抢nodeint val = dfs(node.left, true) + dfs(node.right, true);// 抢劫nodeif (robAble) {val = Math.max(val, node.val + dfs(node.left, false) + dfs(node.right, false));}return val;}
进一步优化:这里有重复解决的子问题。在解决node1的时候,需要解决:抢劫node2,抢劫node3,不抢劫node2,不抢劫node3;在解决抢劫node2需要解决抢和不抢两个问题,而不抢劫node2的问题中也要解决不抢的问题。所以这里是有重复子问题的。我们这里有两种解决方法。一种是消除重复子问题,一种是把子问题的状态保存起来,只算一次。
状态保存,只算一次
public int robV5(TreeNode root) {return dfsV5(root, true,new HashMap<TreeNode,Integer>(),new HashMap<TreeNode,Integer>());}private int dfsV5(TreeNode node, boolean robAble,Map<TreeNode,Integer> canMap,Map<TreeNode,Integer> notMap) {if (node == null) {return 0;}if(robAble && canMap.get(node)!=null) return canMap.get(node);if(!robAble && notMap.get(node)!=null) return notMap.get(node);// 不抢nodeint val = dfsV5(node.left, true,canMap,notMap) + dfsV5(node.right, true,canMap,notMap);notMap.put(node, val);// 抢劫nodeif (robAble) {val = Math.max(val, node.val + dfsV5(node.left, false,canMap,notMap) + dfsV5(node.right, false,canMap,notMap));canMap.put(node, val);}return val;}
消除重复子问题
利用动态规划的思想,算出对于每个节点node ,抢和不抢两种情况下的最大值。最后返回root节点抢和不抢收益的最大值。对于树的动态规划与数组动态规划解决起来有点区别。leetcode上有一道题目是买卖股票的题目。每天可以买股票也可以卖股票。解决方法之一就是要保存每天持有股票、卖出股票的状态。
public int robV4(TreeNode root) {int[] res = robNode(root);return Math.max(res[0], res[1]);}/*** res[0]=不抢劫root的最大收益* res[1]=抢劫root的最大收益* @param root* @return*/private int[] robNode(TreeNode root) {if (root == null)return new int[2];int[] left = robNode(root.left);int[] right = robNode(root.right);return new int[] { Math.max(left[0], left[1]) + Math.max(right[0],right[1]), root.val + left[0] + right[0] };}
代码
494. Target Sum
思路:题目不难。再次是一个典型的DFS到DP。先暴力搜索得到一个思路。对于每个nums[i],给予 +或者-,计算可能性。
接着考虑dp,找到初始条件和递归方程。在nums[i]想要得到S,那么就是 nums[i]-1 的时候得到 S-nums[i] 的方法数+ 得到S+nums[i]的方法数。初始条件是 得到nums[0] 的方法数是1 ,得到-nums[0]的方法数是1。
写递归版的DP;加上缓存的DP;把递归改为迭代的DP;优化缓存的DP。我依然停在了递归改迭代。看来进入DP专题的时候,这块我要多多练习。
代码