题目列表

组合

  • 77. 组合(中等)
  • 17. 电话号码的字母组合(中等)
  • 39. 组合总和(中等)
  • 40. 组合总和 II(中等)
  • 216. 组合总和 III

    分割

  • 93. 复原 IP 地址(中等)

  • 131. 分割回文串

    子集

  • 78. 子集(中等)

  • 90. 子集 II(中等):剪枝技巧同 47 题、39 题、40 题;

    排列

  • 46. 全排列(中等)

  • 47. 全排列 II(中等):思考为什么造成了重复,如何在搜索之前就判断这一支会产生重复;
  • 60. 第 k 个排列(中等):利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点;

    Flood Fill

  • 733. 图像渲染(Flood Fill,中等)

  • 200. 岛屿数量(中等)
  • 130. 被围绕的区域(中等)
  • 79. 单词搜索(中等)

    字符串中的回溯问题

  • 784. 字母大小写全排列(中等)

  • 22. 括号生成(中等):这道题广度优先遍历也很好写,可以通过这个问题理解一下为什么回溯算法都是深度优先遍历,并且都用递归来写。

    游戏问题

  • 51. N 皇后(困难):其实就是全排列问题,注意设计清楚状态变量,在遍历的时候需要记住一些信息,空间换时间;

  • 37. 解数独(困难):思路同「N 皇后问题」;
  • 488. 祖玛游戏(困难)
  • 529. 扫雷游戏(困难)

    具体实现

    组合

    77. 组合

    1. class Solution {
    2. List<List<Integer>> res;
    3. public void dfs(int level, int start, ArrayList path, int n, int k){
    4. if(level == k){
    5. res.add(new ArrayList(path));
    6. return;
    7. }
    8. for(int i = start; i <= n; i++){
    9. path.add(i);
    10. dfs(level + 1, i + 1, path, n, k);
    11. path.remove(path.size() - 1);
    12. }
    13. }
    14. public List<List<Integer>> combine(int n, int k) {
    15. res = new ArrayList<>();
    16. ArrayList<Integer> path = new ArrayList<>();
    17. dfs(0, 1, path, n, k);
    18. return res;
    19. }
    20. }

    17. 电话号码的字母组合

    1. class Solution {
    2. List<String> res;
    3. String[] snum= new String[]{
    4. "","",
    5. "abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"
    6. };
    7. public void dfs(int level, String digits, String path){
    8. if(level == digits.length()){
    9. res.add(path);
    10. return;
    11. }
    12. int t = digits.charAt(level) - '0';
    13. String tmp = snum[t];
    14. for(int i = 0; i < tmp.length(); i++){
    15. dfs(level + 1, digits, path + tmp.charAt(i));
    16. }
    17. }
    18. public List<String> letterCombinations(String digits) {
    19. res = new ArrayList<>();
    20. if(digits.length() == 0) return res;
    21. String path = "";
    22. dfs(0, digits, path);
    23. return res;
    24. }
    25. }

    39. 组合总和(中等)

    1. class Solution {
    2. List<List<Integer>> res;
    3. public void dfs(int start, int sum, ArrayList path, int[] candidates, int target){
    4. if(sum >= target){
    5. if(sum == target) res.add(new ArrayList(path));
    6. return;
    7. }
    8. for(int i = start; i < candidates.length; i++){
    9. path.add(candidates[i]);
    10. dfs(i, sum + candidates[i], path, candidates, target);
    11. path.remove(path.size() - 1);
    12. }
    13. }
    14. public List<List<Integer>> combinationSum(int[] candidates, int target) {
    15. res = new ArrayList<>();
    16. ArrayList path = new ArrayList<>();
    17. dfs(0, 0, path, candidates, target);
    18. return res;
    19. }
    20. }

    40. 组合总和 II(中等)

    1. class Solution {
    2. List<List<Integer>> res;
    3. public void dfs(int start, int sum, ArrayList path, int[] candidates, int target){
    4. if(sum >= target){
    5. if(sum == target) res.add(new ArrayList(path));
    6. return;
    7. }
    8. for(int i = start; i < candidates.length; i++){
    9. if(i > start && candidates[i] == candidates[i - 1]) continue;
    10. path.add(candidates[i]);
    11. dfs(i + 1, sum + candidates[i], path, candidates, target);
    12. path.remove(path.size() - 1);
    13. }
    14. }
    15. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    16. res = new ArrayList<>();
    17. Arrays.sort(candidates);
    18. ArrayList path = new ArrayList<>();
    19. dfs(0, 0, path, candidates, target);
    20. return res;
    21. }
    22. }

    216. 组合总和 III

    class Solution {
      int[] choose = new int[]{1,2,3,4,5,6,7,8,9};
      List<List<Integer>> res;
      public void dfs(int level, int start, int sum, int k, int n, ArrayList path){
          if(level == k){
              if(sum == n) res.add(new ArrayList(path));
              return;
          }
          for(int i = start; i < choose.length; i++){
              path.add(choose[i]);
              dfs(level + 1, i + 1, sum + choose[i], k, n, path);
              path.remove(path.size() - 1);
          }
      }
      public List<List<Integer>> combinationSum3(int k, int n) {
          res = new ArrayList<>();
          ArrayList<Integer> path = new ArrayList<>();
          dfs(0, 0, 0, k, n, path);
          return res;
      }
    }
    

    排列

    46. 全排列

    class Solution {
      List<List<Integer>> res;
      boolean[] f;
      public void dfs(int level, int[] nums, ArrayList path){
          if(level == nums.length){
              /*
              如果直接res.add(path);会发现res里面全部都是空的列表对象
              变量 path 所指向的列表 在深度优先遍历的过程中只有一份 ,
              深度优先遍历完成以后,回到了根结点,成为空列表。
              在 Java 中,参数传递是值传递,对象类型变量在传参的过程中,复制的是变量的地址。
              这些地址被添加到res变量,但实际上指向的是同一块内存地址,
              因此我们会看到空的列表对象。解决的方法很简单,这里做一次拷贝即可。
              */
              res.add(new ArrayList(path));
              return;
          }
          for(int i = 0; i < nums.length; i++){
              if(f[i] == false){
                  f[i] = true;
                  path.add(nums[i]);
                  dfs(level + 1, nums, path);
                  f[i] = false;
                  path.remove(path.size() - 1);
              }
          }
      }
      public List<List<Integer>> permute(int[] nums) {
          res = new ArrayList<>();
          f = new boolean[nums.length];
          ArrayList path = new ArrayList<>();
          dfs(0, nums, path);
          return res;
      }
    }
    

    47. 全排列 II(中等)

  • if(i > 0 && nums[i] == nums[i - 1] && f[i - 1] == false) continue;

  • 如果这个数和之前的数一样,并且之前的数还未使用过,说明当前的这种情况已经回溯过了,为了避免重复就可以跳过
    class Solution {
      List<List<Integer>> res;
      boolean[] f;
      public void dfs(int level, int[] nums, ArrayList path){
          if(level == nums.length){
              res.add(new ArrayList(path));
              return;
          }
          for(int i = 0; i < nums.length; i++){
              if(f[i] == false){
                  if(i > 0 && nums[i] == nums[i - 1] && f[i - 1] == false) continue;
                  f[i] = true;
                  path.add(nums[i]);
                  dfs(level + 1, nums, path);
                  f[i] = false;
                  path.remove(path.size() - 1);
              }
          }
      } 
      public List<List<Integer>> permuteUnique(int[] nums) {
          res = new ArrayList<>();
          f = new boolean[nums.length];
          ArrayList<Integer> path = new ArrayList<>();
          Arrays.sort(nums);
          dfs(0, nums, path);
          return res;
      }
    }
    

    Flood Fill

    200. 岛屿数量

    class Solution {
      int res = 0;
      int[] dx = new int[]{-1, 1, 0, 0};
      int[] dy = new int[]{0, 0, -1, 1};
      public void dfs(int i, int j, char[][] grid){
          grid[i][j] = '0';
          for(int x = 0; x < 4; x++){
              int a = i + dx[x];
              int b = j + dy[x];
              if(a >= 0 && a < grid.length && b >= 0 && b < grid[0].length && grid[a][b] == '1'){
                  dfs(a, b, grid);
              }
          }
      }
      public int numIslands(char[][] grid) {
          for(int i = 0; i < grid.length; i++){
              for(int j = 0; j < grid[0].length; j++){
                  if(grid[i][j] == '1'){
                      dfs(i, j, grid);
                      res++;
                  }
              }
          }
          return res;
      }
    }
    

    130. 被围绕的区域

    从四周开始突围,突围成功的标记成’.‘,突围失败的最后变成x
    class Solution {
      int[] dx = new int[]{-1, 1, 0, 0};
      int[] dy = new int[]{0, 0, -1, 1};
      public void dfs(int i, int j, char[][] board){
          board[i][j] = '.';
          for(int x = 0; x < 4; x++){
              int a = i + dx[x];
              int b = j + dy[x];
              if(a >= 0 && a < board.length && b >= 0 && b < board[0].length && board[a][b] == 'O'){
                  dfs(a, b, board);
              }
          }
      }
      public void solve(char[][] board) {
          int m = board.length;
          int n = board[0].length;
          for(int i = 0; i < m; i++){
              if(board[i][0] == 'O') dfs(i, 0, board);
              if(board[i][n - 1] == 'O') dfs(i, n - 1, board);
          }
          for(int i = 0; i < n; i++){
              if(board[0][i] == 'O') dfs(0, i, board);
              if(board[m-1][i] == 'O') dfs(m - 1, i, board);
          }
          for(int i = 0; i < m; i++){
              for(int j = 0; j < n; j++){
                  //救不回来的兄弟,就变成x了
                  if(board[i][j] == 'O') board[i][j] = 'X';
                  //救回来的兄弟,恢复成o
                  if(board[i][j] == '.') board[i][j] = 'O';
              }
          }
      }
    }