回溯

题型8:二叉树上的最长路径和

543.二叉树的直径(简单)

  1. class Solution {
  2. private int ans = 0;
  3. public int diameterOfBinaryTree(TreeNode root) {
  4. if(root == null) {
  5. return 0;
  6. }
  7. maxDepth(root);
  8. return ans;
  9. }
  10. private int maxDepth(TreeNode root) {
  11. if(root == null) {
  12. return 0;
  13. }
  14. int left = maxDepth(root.left);
  15. int right = maxDepth(root.right);
  16. int dia = left + right;
  17. ans = Math.max(ans, dia);
  18. return Math.max(left, right) + 1;
  19. }
  20. }

剑指Offer 34.二叉树中和为某一值的路径(中等)

  1. class Solution {
  2. public List<List<Integer>> pathSum(TreeNode root, int target) {
  3. List<List<Integer>> res = new ArrayList<>();
  4. dfs(root, res, new ArrayList<Integer>(), target, 0);
  5. return res;
  6. }
  7. private void dfs(TreeNode root, List<List<Integer>> res, List<Integer> path, int target, int pathSum) {
  8. if(root == null) {
  9. return;
  10. }
  11. pathSum += root.val;
  12. path.add(root.val);
  13. // 从树的根节点开始往下一直到叶节点所经过的节点形成一条路径,即必须是到叶子节点
  14. if(root.left == null && root.right == null && target == pathSum) {
  15. List<Integer> tmp = new ArrayList<>(path);
  16. res.add(tmp);
  17. path.remove(path.size() - 1);
  18. return;
  19. }
  20. dfs(root.left, res, path, target, pathSum);
  21. dfs(root.right, res, path, target, pathSum);
  22. path.remove(path.size() - 1);
  23. }
  24. }

124.二叉树中的最大路径和 (困难)

解题思路:将问题转化为:以任意节点为转折点的最大路径和,然后以“怎么计算某个节点为转折点的最大路径和?”这个问题出发思考。

  1. class Solution {
  2. private int res = Integer.MIN_VALUE;
  3. public int maxPathSum(TreeNode root) {
  4. dfs(root);
  5. return res;
  6. }
  7. private int dfs(TreeNode root) {
  8. if(root == null) {
  9. return 0;
  10. }
  11. // 左子树的最大路径和
  12. int leftMaxPathSum = dfs(root.left);
  13. // 右子树的最大路径和
  14. int rightMaxPathSum = dfs(root.right);
  15. int max = root.val;
  16. if(leftMaxPathSum > 0) {
  17. max += leftMaxPathSum;
  18. }
  19. if(rightMaxPathSum > 0) {
  20. max += rightMaxPathSum;
  21. }
  22. res = Math.max(res, max);
  23. // 当前节点的最大路径和可能存在以下情况
  24. // 当前节点值 > 左/右子树的最大路径和 + 当前节点值
  25. // 左子树最大路径和 + 当前节点值 > 右子树最大路径和 + 当前节点值
  26. int ret = root.val;
  27. ret = Math.max(ret, leftMaxPathSum + root.val);
  28. ret = Math.max(ret, rightMaxPathSum + root.val);
  29. return ret;
  30. }
  31. }

437. 路径总和III (困难)

BFS + 回溯
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. private int res = 0;
  18. public int pathSum(TreeNode root, int targetSum) {
  19. if(root == null) {
  20. return res;
  21. }
  22. LinkedList<TreeNode> queue = new LinkedList<>();
  23. queue.add(root);
  24. while(!queue.isEmpty()) {
  25. TreeNode node = queue.poll();
  26. if(node.left != null) {
  27. queue.offer(node.left);
  28. }
  29. if(node.right != null) {
  30. queue.offer(node.right);
  31. }
  32. backTrace(node, targetSum, 0);
  33. }
  34. return res;
  35. }
  36. private void backTrace(TreeNode root, int targetSum, int sum) {
  37. if(root == null) {
  38. return;
  39. }
  40. int curSum = sum + root.val;
  41. if(targetSum == curSum) {
  42. res++;
  43. }
  44. backTrace(root.left, targetSum, curSum);
  45. backTrace(root.right, targetSum, curSum);
  46. }
  47. }

前缀和+回溯+递归
  1. class Solution {
  2. // key是前缀和, value是大小为key的前缀和出现的次数
  3. private Map<Integer, Integer> prefixSumCount = new HashMap<>();
  4. public int pathSum(TreeNode root, int targetSum) {
  5. // 前缀和为0的一条路径
  6. prefixSumCount.put(0, 1);
  7. return backTrace(root, targetSum, 0);
  8. }
  9. private int backTrace(TreeNode node, int targetSum, int curSum) {
  10. // 1.递归终止条件
  11. if(node == null) {
  12. return 0;
  13. }
  14. // 2.本层要做的事情
  15. int res = 0;
  16. curSum += node.val;
  17. //---核心代码
  18. // 看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径
  19. // 当前节点->root节点反推,有且仅有一条路径,如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
  20. // currSum-target相当于找路径的起点,起点的sum+target=currSum,当前点到起点的距离就是target
  21. res += prefixSumCount.getOrDefault(curSum - targetSum, 0);
  22. // 更新路径上当前节点前缀和的个数
  23. prefixSumCount.put(curSum, prefixSumCount.getOrDefault(curSum, 0) + 1);
  24. //---核心代码
  25. res += backTrace(node.left, targetSum, curSum);
  26. res += backTrace(node.right, targetSum, curSum);
  27. // 4.回到本层,恢复状态,去除当前节点的前缀和数量
  28. prefixSumCount.put(curSum, prefixSumCount.get(curSum) - 1);
  29. return res;
  30. }
  31. }

树形DP
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. private int count = 0;
  18. public int pathSum(TreeNode root, int targetSum) {
  19. // 树形DP,状态从左右子树转移而来,利用DFS来做
  20. // 用 Map 来存储以每个节点作为root节点的路径情况,key为路径和,value为路径条数
  21. dfs(root, targetSum);
  22. return count;
  23. }
  24. private Map<Integer, Integer> dfs(TreeNode node, int targetSum) {
  25. if(node == null) {
  26. return new HashMap<Integer, Integer>();
  27. }
  28. Map<Integer, Integer> nodeMap = new HashMap();
  29. nodeMap.put(node.val, 1);
  30. Map<Integer, Integer> leftNodeMap = dfs(node.left, targetSum);
  31. Map<Integer, Integer> rightNodeMap = dfs(node.right, targetSum);
  32. // 遍历左子节点的Map
  33. for(Map.Entry<Integer, Integer> entry : leftNodeMap.entrySet()) {
  34. Integer newKey = entry.getKey() + node.val;
  35. Integer newValue = entry.getValue();
  36. if(nodeMap.containsKey(newKey)) {
  37. newValue += nodeMap.get(newKey);
  38. }
  39. nodeMap.put(newKey, newValue);
  40. }
  41. // 遍历右子节点的Map
  42. for(Map.Entry<Integer, Integer> entry : rightNodeMap.entrySet()) {
  43. Integer newKey = entry.getKey() + node.val;
  44. Integer newValue = entry.getValue();
  45. if(nodeMap.containsKey(newKey)) {
  46. newValue += nodeMap.get(newKey);
  47. }
  48. nodeMap.put(newKey, newValue);
  49. }
  50. // 遍历当前节点的 Map
  51. for(Map.Entry<Integer, Integer> entry : nodeMap.entrySet()){
  52. if(entry.getKey() == targetSum) {
  53. count += entry.getValue();
  54. }
  55. }
  56. return nodeMap;
  57. }
  58. }

DFS&BFS

剑指Offer 13.机器人的运动范围(中等) DFS(已讲)

DFS

解题思路:
技巧:整数 % 10 能获取个位数,比如 35 % 10 = 5,整数 / 10 能往左移动一位,例如:35 / 10 = 3,所以整数求每位数相加的模板:
假设有一个整数x,整数x的每位相加为t,代码模板如下:

  1. int t = 0;
  2. while(x != 0) {
  3. t += x % 10;
  4. x /= 10;
  5. }
  1. class Solution {
  2. private boolean[][] isVisited;
  3. public int movingCount(int m, int n, int k) {
  4. isVisited = new boolean[m][n];
  5. return dfs(0, 0, m, n, k);
  6. }
  7. private int dfs(int x, int y, int m, int n, int k) {
  8. if(x == m || y == n || x < 0 || y < 0 || count(x, y) > k || isVisited[x][y]) {
  9. return 0;
  10. }
  11. isVisited[x][y] = true;
  12. return 1 + dfs(x + 1, y, m, n, k) + dfs(x, y + 1, m, n, k) + dfs(x - 1, y, m, n, k) + dfs(x, y - 1, m, n, k) ;
  13. }
  14. private int count(int x, int y) {
  15. int t = 0;
  16. while(x != 0) {
  17. t+= x % 10;
  18. x /= 10;
  19. }
  20. while(y != 0) {
  21. t += y % 10;
  22. y /= 10;
  23. }
  24. return t;
  25. }
  26. }

面试题08.10.颜色填充(简单) DFS

DFS

  1. class Solution {
  2. public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
  3. // 防止重复遍历,造成死循环
  4. boolean[][] visited = new boolean[image.length][image[0].length];
  5. dfs(image, sr, sc, image[sr][sc], newColor, visited);
  6. return image;
  7. }
  8. private void dfs(int[][] image, int sr, int sc, int initColor, int newColor, boolean[][] visited) {
  9. if(sr < 0 || sr >= image.length || sc < 0 || sc >= image[0].length || visited[sr][sc]) {
  10. return;
  11. }
  12. if(initColor != image[sr][sc]) {
  13. return;
  14. }
  15. image[sr][sc] = newColor;
  16. visited[sr][sc] = true;
  17. dfs(image, sr-1, sc, initColor, newColor, visited);
  18. dfs(image, sr, sc - 1, initColor, newColor, visited);
  19. dfs(image, sr + 1, sc, initColor, newColor, visited);
  20. dfs(image, sr, sc + 1, initColor, newColor, visited);
  21. }
  22. }

面试题04.01.节点间通路(中等)DFS BFS搜索

DFS

  1. class Solution {
  2. public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
  3. // 访问状态数组
  4. boolean[] visited = new boolean[graph.length];
  5. return dfs(graph, visited, start, target);
  6. }
  7. private boolean dfs(int[][] graph, boolean[] visited, int start, int target) {
  8. for(int i = 0; i < graph.length; i++) {
  9. // 确保当前路径未被访问(该判断主要是为了防止图中自环出现死循环的情况)
  10. if(visited[i]) {
  11. continue;
  12. }
  13. if(graph[i][0] == start && graph[i][1] == target) {
  14. return true;
  15. }
  16. // 设置访问标志
  17. visited[i] = true;
  18. // 核心代码,这里确实巧妙,利用目标值反推起始值,逐渐压缩搜索区间,减少递归次数,剪枝操作
  19. if(graph[i][1] == target && dfs(graph, visited, start, graph[i][0])) {
  20. return true;
  21. }
  22. // 清除访问标志
  23. visited[i] = false;
  24. }
  25. return false;
  26. }
  27. }

DFS-优化版

  1. class Solution {
  2. public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
  3. if(start == target) {
  4. return true;
  5. }
  6. for(int[] g : graph) {
  7. // 核心,利用目标值反推起始值,逐渐压缩搜索区间,减少递归次数,剪枝操作
  8. if(g[1] == target) {
  9. return findWhetherExistsPath(n, graph, start, g[0]);
  10. }
  11. }
  12. return false;
  13. }
  14. }

邻接表

  1. class Solution {
  2. public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
  3. // 构建邻接表
  4. HashSet<Integer>[] map = new HashSet[n];
  5. for(int[] g : graph) {
  6. if(map[g[0]] == null) {
  7. map[g[0]] = new HashSet<Integer>();
  8. }
  9. if(!map[g[0]].contains(g[1])) {
  10. map[g[0]].add(g[1]);
  11. }
  12. }
  13. boolean[] visited = new boolean[n];
  14. return dfs(map, visited, start, target);
  15. }
  16. private boolean dfs(HashSet<Integer>[] map, boolean[] visited, int cur, int target) {
  17. if(cur == target) {
  18. return true;
  19. }
  20. if(map[cur] == null) {
  21. return false;
  22. }
  23. boolean found = false;
  24. visited[cur] = true;
  25. for(Integer idx : map[cur]) {
  26. if(!visited[idx]) {
  27. found = found || dfs(map, visited, idx, target);
  28. }
  29. }
  30. visited[cur] = false;
  31. return found;
  32. }
  33. }

200.岛屿数量(中等) DFS求连通分量(已讲)

DFS

  1. class Solution {
  2. public int numIslands(char[][] grid) {
  3. int m = grid.length;
  4. int n = grid[0].length;
  5. int count = 0;
  6. boolean[][] visited = new boolean[m][n];
  7. for(int i = 0; i < m; i++) {
  8. for(int j = 0; j < n; j++) {
  9. // 是陆地且未被访问过的
  10. if(grid[i][j] == '1' && !visited[i][j]) {
  11. helper(grid, i, j, visited);
  12. count++;
  13. }
  14. }
  15. }
  16. return count;
  17. }
  18. private void helper(char[][] grid, int m, int n, boolean[][] visited) {
  19. if(n < 0 || n == grid[0].length || m < 0 || m == grid.length || visited[m][n] || grid[m][n] == '0') {
  20. return;
  21. }
  22. // 标记该点已被访问
  23. visited[m][n] = true;
  24. // 向上移动
  25. helper(grid, m - 1, n, visited);
  26. // 向下移动
  27. helper(grid, m + 1, n, visited);
  28. // 向左移动
  29. helper(grid, m, n - 1, visited);
  30. // 向右移动
  31. helper(grid, m, n + 1, visited);
  32. }
  33. }

面试题16.19.水域大小(中等) DFS连通性

DFS

  1. class Solution {
  2. public int[] pondSizes(int[][] land) {
  3. List<Integer> res = new ArrayList<>();
  4. boolean[][] visited = new boolean[land.length][land[0].length];
  5. for(int m = 0; m < land.length; m++) {
  6. for(int n = 0; n < land[0].length; n++) {
  7. if(!visited[m][n] && land[m][n] == 0) {
  8. int size = dfs(land, m, n, visited);
  9. res.add(size);
  10. }
  11. }
  12. }
  13. int[] result = new int[res.size()];
  14. for(int i = 0; i < res.size(); i++) {
  15. result[i] = res.get(i);
  16. }
  17. Arrays.sort(result);
  18. return result;
  19. }
  20. private int dfs(int[][] land, int m, int n, boolean[][] visited) {
  21. if(m < 0 || m >= land.length || n < 0 || n >= land[0].length || visited[m][n] || land[m][n] > 0) {
  22. return 0;
  23. }
  24. visited[m][n] = true;
  25. // 上、下、左、右、左上、左下、右上、右下
  26. int size = 0;
  27. size += dfs(land, m - 1, n, visited);
  28. size += dfs(land, m + 1, n, visited);
  29. size += dfs(land, m, n -1, visited);
  30. size += dfs(land, m, n + 1, visited);
  31. size += dfs(land, m + 1, n + 1, visited);
  32. size += dfs(land, m + 1, n - 1, visited);
  33. size += dfs(land, m - 1, n + 1, visited);
  34. size += dfs(land, m - 1, n - 1, visited);
  35. return size + 1;
  36. }
  37. }

207.课程表(中等) 拓扑排序,看是否存在环,有两种算法Kahn/DFS

DFS

  1. class Solution {
  2. private boolean res = true;
  3. private int size = 0;
  4. public boolean canFinish(int numCourses, int[][] prerequisites) {
  5. boolean[] visited = new boolean[prerequisites.length];
  6. for(int i = 0; i < numCourses && res; i++) {
  7. dfs(i, prerequisites, new ArrayList<Integer>(), visited);
  8. }
  9. return res;
  10. }
  11. private void dfs(int premise, int[][] prerequisites, List<Integer> path, boolean[] visited) {
  12. if(path.contains(premise)) {
  13. res = false;
  14. return;
  15. }
  16. path.add(premise);
  17. for(int i = 0; i < prerequisites.length & res; i++) {
  18. if(prerequisites[i][0] == premise && !visited[i]) {
  19. visited[i] = true;
  20. dfs(prerequisites[i][1], prerequisites, path, visited);
  21. path.remove(path.size() - 1);
  22. }
  23. }
  24. }
  25. }

DFS - 优化写法

  1. class Solution {
  2. List<List<Integer>> edges;
  3. int[] visited;
  4. boolean valid = true;
  5. public boolean canFinish(int numCourses, int[][] prerequisites) {
  6. edges = new ArrayList<List<Integer>>();
  7. for (int i = 0; i < numCourses; ++i) {
  8. edges.add(new ArrayList<Integer>());
  9. }
  10. visited = new int[numCourses];
  11. for (int[] info : prerequisites) {
  12. edges.get(info[1]).add(info[0]);
  13. }
  14. for (int i = 0; i < numCourses && valid; ++i) {
  15. if (visited[i] == 0) {
  16. dfs(i);
  17. }
  18. }
  19. return valid;
  20. }
  21. public void dfs(int u) {
  22. visited[u] = 1;
  23. for (int v: edges.get(u)) {
  24. if (visited[v] == 0) {
  25. dfs(v);
  26. if (!valid) {
  27. return;
  28. }
  29. } else if (visited[v] == 1) {
  30. valid = false;
  31. return;
  32. }
  33. }
  34. visited[u] = 2;
  35. }
  36. }

邻接矩阵 + DFS

  1. class Solution {
  2. private boolean valid = true;
  3. private List<List<Integer>> table = new ArrayList<>();
  4. public boolean canFinish(int numCourses, int[][] prerequisites) {
  5. // 构建邻接表
  6. for(int i = 0; i < numCourses; i++) {
  7. table.add(new ArrayList<Integer>());
  8. }
  9. // 初始化邻接表
  10. for(int i = 0; i < prerequisites.length; i++) {
  11. table.get(prerequisites[i][1]).add(prerequisites[i][0]);
  12. }
  13. int[] visited = new int[numCourses];
  14. for(int i = 0; i < numCourses && valid; i++) {
  15. if(visited[i] == 0) {
  16. dfs(visited, i);
  17. }
  18. }
  19. return valid;
  20. }
  21. private void dfs(int[] visited, int i) {
  22. visited[i] = 1;
  23. for(int v : table.get(i)) {
  24. if(visited[v] == 1) {
  25. valid = false;
  26. return;
  27. } else if(visited[v] == 0) {
  28. dfs(visited, v);
  29. // 剪枝
  30. if(!valid) {
  31. return;
  32. }
  33. }
  34. }
  35. visited[i] = 2;
  36. }
  37. }

79.单词搜索(中等)DFS的稍微升级

DFS

  1. class Solution {
  2. private boolean res = false;
  3. public boolean exist(char[][] board, String word) {
  4. int m = board.length;
  5. int n = board[0].length;
  6. int idx = 0;
  7. boolean[][] visited = new boolean[m][n];
  8. for(int i = 0; i < m; i++) {
  9. for(int j = 0; j < n; j++) {
  10. if(res) {
  11. break;
  12. }
  13. if(board[i][j] == word.charAt(idx)) {
  14. dfs(board, i, j, visited, word, idx);
  15. }
  16. }
  17. }
  18. return res;
  19. }
  20. private void dfs(char[][] board, int m, int n, boolean[][] visited, String word, int idx) {
  21. if(idx == word.length()) {
  22. res = true;
  23. return;
  24. }
  25. if(res || m < 0 || m == board.length || n < 0 || n == board[0].length || visited[m][n] || board[m][n] != word.charAt(idx)) {
  26. return;
  27. }
  28. visited[m][n] = true;
  29. dfs(board, m - 1, n, visited, word, idx + 1);
  30. dfs(board, m + 1, n, visited, word, idx + 1);
  31. dfs(board, m, n - 1, visited, word, idx + 1);
  32. dfs(board, m, n + 1, visited, word, idx + 1);
  33. visited[m][n] = false;
  34. }
  35. }

1306.跳跃游戏III(中等) DFS,看着不像,实际上是

  1. class Solution {
  2. public boolean canReach(int[] arr, int start) {
  3. return dfs(arr, start, new boolean[arr.length]);
  4. }
  5. private boolean dfs(int[] arr, int start, boolean[] visited) {
  6. if(start < 0 || start >= arr.length || visited[start]) {
  7. return false;
  8. }
  9. if(arr[start] == 0) {
  10. return true;
  11. }
  12. visited[start] = true;
  13. return dfs(arr, start + arr[start], visited) || dfs(arr, start - arr[start], visited);
  14. }
  15. }

752.打开转盘锁(中等) BFS(已讲)

BFS

解题思路:

  1. 以”0000”为根子节点,会扩展出”1000”、”0100”、”0010”、”0001”、”9000”、”0900”、”0090”、”0009” 这些节点,扩展出的节点,根据题意每个节点又能扩展出8个节点,这样就形成了一个多叉树。
  2. 分别用两个 HashSet 来排除死亡数字集合和重复子节点,就能计算出解锁需要的最小旋转次数。 ```java class Solution { public int openLock(String[] deadends, String target) {

    1. if(target.equals("0000")) {
    2. return 0;
    3. }
    4. Set<String> deadendSet = new HashSet<>();
    5. // 初始化 HashSet,用于排除死亡数字
    6. for(String deadend : deadends) {
    7. deadendSet.add(deadend);
    8. }
    9. if(deadendSet.contains("0000")) {
    10. return -1;
    11. }
    12. // 使用 HashSet 去除重复节点
    13. Set<String> visited = new HashSet<>();
    14. Queue<String> queue = new LinkedList<>();
    15. queue.offer("0000");
    16. visited.add("0000");
    17. int step = 0;
    18. while(!queue.isEmpty()) {
    19. step++;
    20. int size = queue.size();
    21. for(int i = 0; i < size; i++) {
    22. String node = queue.poll();
    23. List<String> newNodes = genNewNodes(node);
    24. for(String newNode : newNodes) {
    25. // 优化点,放外层的话,需要多遍历几个节点
    26. if(newNode.equals(target)) {
    27. return step;
    28. }
    29. // 排除死亡数字 或者 排除重复节点
    30. if(visited.contains(newNode) || deadendSet.contains(newNode)) {
    31. continue;
    32. }
    33. queue.offer(newNode);
    34. visited.add(newNode);
    35. }
    36. }
    37. }
    38. return -1;

    }

    public char numPrev(char x) {

    1. return x == '0' ? '9' : (char) (x - 1);

    }

    public char numSucc(char x) {

    1. return x == '9' ? '0' : (char) (x + 1);

    }

  1. private List<String> genNewNodes(String node) {
  2. List<String> newNodes = new ArrayList<>();
  3. char[] array = node.toCharArray();
  4. for(int i = 0; i < node.length(); i++) {
  5. char num = array[i];
  6. char oldVal = array[i];
  7. array[i] = numPrev(oldVal);
  8. newNodes.add(new String(array));
  9. array[i] = numSucc(oldVal);
  10. newNodes.add(new String(array));
  11. array[i] = num;
  12. }
  13. return newNodes;
  14. }

}

  1. <a name="xoZ6M"></a>
  2. #### 双向BFS
  3. ```java
  4. class Solution {
  5. public int openLock(String[] deadends, String target) {
  6. // 记录已访问过的记录
  7. Set<String> visited = new HashSet<>();
  8. // 初始化
  9. for(String deadend : deadends) {
  10. visited.add(deadend);
  11. }
  12. Set<String> q1 = new HashSet<>();
  13. Set<String> q2 = new HashSet<>();
  14. q1.add("0000");
  15. q2.add(target);
  16. int step = 0;
  17. while(!q1.isEmpty() && !q2.isEmpty()) {
  18. if(q1.size() > q2.size()) {
  19. Set<String> temp = q1;
  20. q1 = q2;
  21. q2 = temp;
  22. }
  23. Set<String> temp = new HashSet<>();
  24. for(String cur : q1) {
  25. if(visited.contains(cur)) {
  26. continue;
  27. }
  28. if(q2.contains(cur)) {
  29. return step;
  30. }
  31. // 添加已访问的记录
  32. visited.add(cur);
  33. for(int i = 0; i < 4; i++) {
  34. String up = plusOne(cur, i);
  35. if(!visited.contains(up)) {
  36. temp.add(up);
  37. }
  38. String down = minusOne(cur, i);
  39. if(!visited.contains(down)) {
  40. temp.add(down);
  41. }
  42. }
  43. }
  44. step++;
  45. q1 = q2;
  46. q2 = temp;
  47. }
  48. return -1;
  49. }
  50. private String plusOne(String s, int i) {
  51. char[] array = s.toCharArray();
  52. if(array[i] == '9') {
  53. array[i] = '0';
  54. } else {
  55. array[i] += 1;
  56. }
  57. return new String(array);
  58. }
  59. private String minusOne(String s, int i) {
  60. char[] array = s.toCharArray();
  61. if(array[i] == '0') {
  62. array[i] = '9';
  63. } else {
  64. array[i] -= 1;
  65. }
  66. return new String(array);
  67. }
  68. }

以下选做:

面试题17.22.单词转换(困难) DFS 流程是标准DFS,但背景要抽象一下

解题思路:常规的 DFS 解法
注意点:

  • beginWord 必然在结果集中
  • endWord 需要在 wordList 中
  • beginWord 每一次替换字符的状态,都需要在 wordList 集合中
  • 通过建立 visited 集合来记录已转换过的状态,达成剪枝的目的
  1. class Solution {
  2. public List<String> findLadders(String beginWord, String endWord, List<String> wordList) {
  3. Set<String> wordSet = new HashSet<>(wordList);
  4. Set<String> visitedSet = new HashSet<>();
  5. List<String> res = new ArrayList<>();
  6. // 边界判断
  7. if(!wordSet.contains(endWord)) {
  8. return res;
  9. }
  10. res.add(beginWord);
  11. visitedSet.add(beginWord);
  12. if(!dfs(beginWord.toCharArray(), endWord.toCharArray(), wordSet, visitedSet, res)) {
  13. return new ArrayList<>();
  14. }
  15. return res;
  16. }
  17. private boolean dfs(char[] beginWord, char[] endWord, Set<String> wordSet, Set<String> visitedSet, List<String> path) {
  18. // 终止条件
  19. if(Arrays.equals(beginWord, endWord)) {
  20. return true;
  21. }
  22. for(int i = 0; i < beginWord.length; i++) {
  23. for(int j = 0; j < 26; j++) {
  24. char oldChar = beginWord[i];
  25. beginWord[i] = (char)('a' + j);
  26. String word = new String(beginWord);
  27. if(wordSet.contains(word) && !visitedSet.contains(word)) {
  28. path.add(word);
  29. visitedSet.add(word);
  30. if(dfs(beginWord, endWord, wordSet, visitedSet, path)) {
  31. return true;
  32. }
  33. // 回溯
  34. path.remove(path.size() - 1);
  35. }
  36. // 复原
  37. beginWord[i] = oldChar;
  38. }
  39. }
  40. return false;
  41. }
  42. }

面试题17.07.婴儿名字(困难) 关系是固定的,并查集或者DFS都能搞定! 关键在于将数据转化成图结构,也就是建模烦!

529.扫雷游戏(困难)

127.单词接龙(困难)

126. 单词接龙II(困难)