diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index e18a69e9..8bed20d1 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -874,28 +874,85 @@ private class Position { ``` ```java -public int maxAreaOfIsland(int[][] grid) { - int max = 0; - for (int i = 0; i < grid.length; i++) { - for (int j = 0; j < grid[i].length; j++) { - if (grid[i][j] == 1) { - max = Math.max(max, dfs(grid, i, j)); - } - } - } - return max; -} +private int m, n; +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; -private int dfs(int[][] grid, int i, int j) { - if (i < 0 || i >= grid.length || j < 0 || j >= grid[i].length || grid[i][j] == 0) { +public int maxAreaOfIsland(int[][] grid) { + if (grid == null || grid.length == 0) { return 0; } - grid[i][j] = 0; - return dfs(grid, i + 1, j) + dfs(grid, i - 1, j) + dfs(grid, i, j + 1) + dfs(grid, i, j - 1) + 1; + m = grid.length; + n = grid[0].length; + int maxArea = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + maxArea = Math.max(maxArea, dfs(grid, i, j)); + } + } + return maxArea; +} + +private int dfs(int[][] grid, int r, int c) { + if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) { + return 0; + } + grid[r][c] = 0; + int area = 1; + for (int[] d : direction) { + area += dfs(grid, r + d[0], c + d[1]); + } + return area; } ``` -**图的连通分量** +**矩阵中的连通分量数目** + +[Leetcode : 200. Number of Islands (Medium)](https://leetcode.com/problems/number-of-islands/description/) + +```html +11110 +11010 +11000 +00000 +Answer: 1 +``` + +可以将矩阵表示看成一张有向图。 + +```java +private int m, n; +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + m = grid.length; + n = grid[0].length; + int islandsNum = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] != '0') { + dfs(grid, i, j); + islandsNum++; + } + } + } + return islandsNum; +} + +private void dfs(char[][] grid, int i, int j) { + if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') { + return; + } + grid[i][j] = '0'; + for (int k = 0; k < direction.length; k++) { + dfs(grid, i + direction[k][0], j + direction[k][1]); + } +} +``` + +**好友关系的连通分量数目** [Leetcode : 547. Friend Circles (Medium)](https://leetcode.com/problems/friend-circles/description/) @@ -909,24 +966,28 @@ Explanation:The 0th and 1st students are direct friends, so they are in a friend The 2nd student himself is in a friend circle. So return 2. ``` +好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。 + ```java +private int n; + public int findCircleNum(int[][] M) { - int n = M.length; - int ret = 0; + n = M.length; + int circleNum = 0; boolean[] hasVisited = new boolean[n]; for (int i = 0; i < n; i++) { if (!hasVisited[i]) { dfs(M, i, hasVisited); - ret++; + circleNum++; } } - return ret; + return circleNum; } private void dfs(int[][] M, int i, boolean[] hasVisited) { hasVisited[i] = true; - for (int k = 0; k < M.length; k++) { + for (int k = 0; k < n; k++) { if (M[i][k] == 1 && !hasVisited[k]) { dfs(M, k, hasVisited); } @@ -934,117 +995,6 @@ private void dfs(int[][] M, int i, boolean[] hasVisited) { } ``` -**矩阵中的连通区域数量** - -[Leetcode : 200. Number of Islands (Medium)](https://leetcode.com/problems/number-of-islands/description/) - -```html -11110 -11010 -11000 -00000 -Answer: 1 -``` - -```java -private int m, n; -private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; - -public int numIslands(char[][] grid) { - if (grid == null || grid.length == 0) return 0; - m = grid.length; - n = grid[0].length; - int ret = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (grid[i][j] == '1') { - dfs(grid, i, j); - ret++; - } - } - } - return ret; -} - -private void dfs(char[][] grid, int i, int j) { - if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') return; - grid[i][j] = '0'; - for (int k = 0; k < direction.length; k++) { - dfs(grid, i + direction[k][0], j + direction[k][1]); - } -} -``` - -**输出二叉树中所有从根到叶子的路径** - -[Leetcode : 257. Binary Tree Paths (Easy)](https://leetcode.com/problems/binary-tree-paths/description/) - -```html - 1 - / \ -2 3 -\ - 5 -``` -```html -["1->2->5", "1->3"] -``` - -```java -public List<String> binaryTreePaths(TreeNode root) { - List<String> ret = new ArrayList(); - if(root == null) return ret; - dfs(root, "", ret); - return ret; -} - -private void dfs(TreeNode root, String prefix, List<String> ret){ - if(root == null) return; - if(root.left == null && root.right == null){ - ret.add(prefix + root.val); - return; - } - prefix += (root.val + "->"); - dfs(root.left, prefix, ret); - dfs(root.right, prefix, ret); -} -``` - -**IP 地址划分** - -[Leetcode : 93. Restore IP Addresses(Medium)](https://leetcode.com/problems/restore-ip-addresses/description/) - -```html -Given "25525511135", -return ["255.255.11.135", "255.255.111.35"]. -``` - -```java -private List<String> ret; - -public List<String> restoreIpAddresses(String s) { - ret = new ArrayList<>(); - doRestore(0, "", s); - return ret; -} - -private void doRestore(int k, String path, String s) { - if (k == 4 || s.length() == 0) { - if (k == 4 && s.length() == 0) { - ret.add(path); - } - return; - } - for (int i = 0; i < s.length() && i <= 2; i++) { - if (i != 0 && s.charAt(0) == '0') break; - String part = s.substring(0, i + 1); - if (Integer.valueOf(part) <= 255) { - doRestore(k + 1, path.length() != 0 ? path + "." + part : part, s.substring(i + 1)); - } - } -} -``` - **填充封闭区域** [Leetcode : 130. Surrounded Regions (Medium)](https://leetcode.com/problems/surrounded-regions/description/) @@ -1094,8 +1044,8 @@ public void solve(char[][] board) { private void dfs(char[][] board, int r, int c) { if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') return; board[r][c] = 'T'; - for (int i = 0; i < direction.length; i++) { - dfs(board, r + direction[i][0], c + direction[i][1]); + for (int[] d : direction) { + dfs(board, r + d[0], c + d[1]); } } ``` @@ -1129,8 +1079,8 @@ private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; public List<int[]> pacificAtlantic(int[][] matrix) { List<int[]> ret = new ArrayList<>(); if (matrix == null || matrix.length == 0) return ret; - this.m = matrix.length; - this.n = matrix[0].length; + m = matrix.length; + n = matrix[0].length; this.matrix = matrix; boolean[][] canReachP = new boolean[m][n]; boolean[][] canReachA = new boolean[m][n]; @@ -1153,11 +1103,11 @@ public List<int[]> pacificAtlantic(int[][] matrix) { } private void dfs(int r, int c, boolean[][] canReach) { - if(canReach[r][c]) return; + if (canReach[r][c]) return; canReach[r][c] = true; - for (int i = 0; i < direction.length; i++) { - int nextR = direction[i][0] + r; - int nextC = direction[i][1] + c; + for (int[] d : direction) { + int nextR = d[0] + r; + int nextC = d[1] + c; if (nextR < 0 || nextR >= m || nextC < 0 || nextC >= n || matrix[r][c] > matrix[nextR][nextC]) continue; dfs(nextR, nextC, canReach); @@ -1167,9 +1117,15 @@ private void dfs(int r, int c, boolean[][] canReach) { ### Backtracking -回溯属于 DF,它不是用在遍历图的节点上,而是用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串。 +Backtracking(回溯)属于 DFS。 -在程序实现时,回溯需要注意对元素进行标记的问题。使用递归实现的回溯,在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;但是在递归返回时,需要将该元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。 +- 普通 DFS 主要用在 **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。 +- 而 Backtracking 主要用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回时,在返回之后还会继续执行求解过程。 + +因为 Backtracking 不是立即就返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题: + +- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素; +- 但是在递归返回时,需要将该元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。 **数字键盘组合** @@ -1199,13 +1155,50 @@ private void combination(StringBuilder prefix, String digits, List<String> ret) } String letters = KEYS[digits.charAt(prefix.length()) - '0']; for (char c : letters.toCharArray()) { - prefix.append(c); + prefix.append(c); // 添加 combination(prefix, digits, ret); prefix.deleteCharAt(prefix.length() - 1); // 删除 } } ``` +**IP 地址划分** + +[Leetcode : 93. Restore IP Addresses(Medium)](https://leetcode.com/problems/restore-ip-addresses/description/) + +```html +Given "25525511135", +return ["255.255.11.135", "255.255.111.35"]. +``` + +```java +public List<String> restoreIpAddresses(String s) { + List<String> addresses = new ArrayList<>(); + StringBuilder path = new StringBuilder(); + doRestore(0, path, s, addresses); + return addresses; +} + +private void doRestore(int k, StringBuilder path, String s, List<String> addresses) { + if (k == 4 || s.length() == 0) { + if (k == 4 && s.length() == 0) { + addresses.add(path.toString()); + } + return; + } + for (int i = 0; i < s.length() && i <= 2; i++) { + if (i != 0 && s.charAt(0) == '0') break; + String part = s.substring(0, i + 1); + if (Integer.valueOf(part) <= 255) { + if (path.length() != 0) part = "." + part; + path.append(part); + doRestore(k + 1, path, s.substring(i + 1), addresses); + path.delete(path.length() - part.length(), path.length()); + } + } +} +``` + **在矩阵中寻找字符串** [Leetcode : 79. Word Search (Medium)](https://leetcode.com/problems/word-search/description/) @@ -1224,8 +1217,7 @@ word = "ABCB", -> returns false. ``` ```java -private static int[][] shift = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; -private static boolean[][] visited; +private static int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; private int m; private int n; @@ -1234,16 +1226,16 @@ public boolean exist(char[][] board, String word) { if (board == null || board.length == 0 || board[0].length == 0) return false; m = board.length; n = board[0].length; - visited = new boolean[m][n]; + boolean[][] visited = new boolean[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { - if (dfs(board, word, 0, i, j)) return true; + if (dfs(board, visited, word, 0, i, j)) return true; } } return false; } -private boolean dfs(char[][] board, String word, int start, int r, int c) { +private boolean dfs(char[][] board, boolean[][] visited, String word, int start, int r, int c) { if (start == word.length()) { return true; } @@ -1251,10 +1243,8 @@ private boolean dfs(char[][] board, String word, int start, int r, int c) { return false; } visited[r][c] = true; - for (int i = 0; i < shift.length; i++) { - int nextR = r + shift[i][0]; - int nextC = c + shift[i][1]; - if (dfs(board, word, start + 1, nextR, nextC)) { + for (int[] d : direction) { + if (dfs(board, visited, word, start + 1, r + d[0], c + d[1])) { return true; } } @@ -1263,6 +1253,59 @@ private boolean dfs(char[][] board, String word, int start, int r, int c) { } ``` +**输出二叉树中所有从根到叶子的路径** + +[Leetcode : 257. Binary Tree Paths (Easy)](https://leetcode.com/problems/binary-tree-paths/description/) + +```html + 1 + / \ +2 3 +\ + 5 +``` + +```html +["1->2->5", "1->3"] +``` + +```java +public List<String> binaryTreePaths(TreeNode root) { + List<String> paths = new ArrayList(); + if (root == null) return paths; + List<Integer> values = new ArrayList<>(); + dfs(root, values, paths); + return paths; +} + +private void dfs(TreeNode node, List<Integer> values, List<String> paths) { + if (node == null) return; + values.add(node.val); + if (isLeaf(node)) { + paths.add(buildPath(values)); + } else { + dfs(node.left, values, paths); + dfs(node.right, values, paths); + } + values.remove(values.size() - 1); +} + +private boolean isLeaf(TreeNode node) { + return node.left == null && node.right == null; +} + +private String buildPath(List<Integer> values) { + StringBuilder str = new StringBuilder(); + for (int i = 0; i < values.size(); i++) { + str.append(values.get(i)); + if (i != values.size() - 1) { + str.append("->"); + } + } + return str.toString(); +} +``` + **排列** [Leetcode : 46. Permutations (Medium)](https://leetcode.com/problems/permutations/description/) @@ -1288,14 +1331,13 @@ public List<List<Integer>> permute(int[] nums) { return ret; } -private void backtracking(List<Integer> permuteList, boolean[] visited, int[] nums, List<List<Integer>> ret){ - if(permuteList.size() == nums.length){ - ret.add(new ArrayList(permuteList)); +private void backtracking(List<Integer> permuteList, boolean[] visited, int[] nums, List<List<Integer>> ret) { + if (permuteList.size() == nums.length) { + ret.add(new ArrayList(permuteList)); // 重新构造一个 List return; } - - for(int i = 0; i < visited.length; i++){ - if(visited[i]) continue; + for (int i = 0; i < visited.length; i++) { + if (visited[i]) continue; visited[i] = true; permuteList.add(nums[i]); backtracking(permuteList, visited, nums, ret); @@ -1330,7 +1372,7 @@ public List<List<Integer>> permuteUnique(int[] nums) { private void backtracking(List<Integer> permuteList, boolean[] visited, int[] nums, List<List<Integer>> ret) { if (permuteList.size() == nums.length) { - ret.add(new ArrayList(permuteList)); // 重新构造一个 List + ret.add(new ArrayList(permuteList)); return; } @@ -1370,16 +1412,16 @@ public List<List<Integer>> combine(int n, int k) { return ret; } -private void backtracking(int start, int n, int k, List<Integer> combineList, List<List<Integer>> ret){ - if(k == 0){ +private void backtracking(int start, int n, int k, List<Integer> combineList, List<List<Integer>> ret) { + if (k == 0) { ret.add(new ArrayList(combineList)); return; } - for(int i = start; i <= n - k + 1; i++) { // 剪枝 - combineList.add(i); // 把 i 标记为已访问 + for (int i = start; i <= n - k + 1; i++) { // 剪枝 + combineList.add(i); backtracking(i + 1, n, k - 1, combineList, ret); - combineList.remove(combineList.size() - 1); // 把 i 标记为未访问 + combineList.remove(combineList.size() - 1); } } ```