内容来自刘宇波老师玩转算法面试
回溯算法解题套路框架
所有方法二实现思路:选择列表(排除不合法的选择 + 做选择 + 进入下一层回溯树 + 取消选择)、路径、结束条件
1、可以把「选择列表」和「路径」作为决策树的每个节点的属性
2、我们定义的「backtrack」函数其实就像一个指针,在这棵树上游走,同时要正确维护每个节点的属性
3、每当走到树的底层叶子节点,其「路径」就是一个解
回溯算法秒杀所有排列 - 组合 - 子集问题
排列有序、组合无序:比如从 1 - 8 号球取 3 个球
1、排列:如果说一个一个拿,拿出来依次是 3 号、2 号、4 号,那么你拿出 234 和 324 是不一样的,这就是排列,有序的
2、组合:如果任意取 3 个球,由于 3 个一起取出,比如你取出的是 123 号球,不存在 123 和 321 有区别,都是这三个,这就是组合,无序的
一文秒杀所有岛屿题目
1、什么是回溯

更多问题
93 - 复原 IP 地址
131 - 分割回文串
2、树形问题
17 - 电话号码的字母组合


2.1、方法一
| public class Solution { |
| |
| private static final String[] letterMap = {" ", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; |
| private static final List<String> res = new ArrayList<>(); |
| |
| public static List<String> letterCombinations(String digits) { |
| res.clear(); |
| if (digits == null || digits.equals("")) return res; |
| |
| findCombination(digits, 0, ""); |
| return res; |
| } |
| |
| |
| |
| |
| |
| private static void findCombination(String digits, int index, String s) { |
| if (index == digits.length()) { |
| res.add(s); |
| return; |
| } |
| |
| char c = digits.charAt(index); |
| String letters = letterMap[c - '0']; |
| for (int i = 0; i < letters.length(); i++) { |
| findCombination(digits, index + 1, s + letters.charAt(i)); |
| } |
| } |
| } |
2.2、方法二
| public class Solution { |
| |
| private static final String[] letterMap = {" ", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; |
| private static final List<String> res = new LinkedList<>(); |
| |
| public static List<String> letterCombinations(String digits) { |
| res.clear(); |
| if (digits == null || digits.equals("")) return res; |
| |
| StringBuilder track = new StringBuilder(); |
| backtrack(digits, 0, track); |
| return res; |
| } |
| |
| |
| |
| |
| private static void backtrack(String digits, int index, StringBuilder track) { |
| |
| if (track.length() == digits.length()) { |
| res.add(track.toString()); |
| return; |
| } |
| |
| char c = digits.charAt(index); |
| String letters = letterMap[c - '0']; |
| for (int i = 0; i < letters.length(); i++) { |
| track.append(letters.charAt(i)); |
| backtrack(digits, index + 1, track); |
| track.deleteCharAt(track.length() - 1); |
| } |
| } |
| } |
3、回溯法是经典人工智能的基础
51 - N 皇后

3.1、图示




3.2、实现
| public class Solution { |
| |
| private static boolean[] col; |
| private static boolean[] dia1; |
| private static boolean[] dia2; |
| private static ArrayList<List<String>> res; |
| |
| public static List<List<String>> solveNQueens(int n) { |
| res = new ArrayList<>(); |
| col = new boolean[n]; |
| dia1 = new boolean[2 * n - 1]; |
| dia2 = new boolean[2 * n - 1]; |
| |
| LinkedList<Integer> row = new LinkedList<>(); |
| backtrack(n, 0, row); |
| |
| return res; |
| } |
| |
| |
| |
| |
| private static void backtrack(int n, int index, LinkedList<Integer> row) { |
| if (index == n) { |
| res.add(generateBoard(n, row)); |
| return; |
| } |
| |
| for (int i = 0; i < n; i++) { |
| |
| if (!col[i] && !dia1[index + i] && !dia2[index - i + n - 1]) { |
| |
| row.addLast(i); |
| col[i] = true; |
| dia1[index + i] = true; |
| dia2[index - i + n - 1] = true; |
| |
| |
| backtrack(n, index + 1, row); |
| |
| |
| col[i] = false; |
| dia1[index + i] = false; |
| dia2[index - i + n - 1] = false; |
| row.removeLast(); |
| } |
| } |
| } |
| |
| private static List<String> generateBoard(int n, LinkedList<Integer> row) { |
| ArrayList<String> board = new ArrayList<>(); |
| for (int i = 0; i < n; i++) { |
| char[] charArray = new char[n]; |
| Arrays.fill(charArray, '.'); |
| charArray[row.get(i)] = 'Q'; |
| board.add(new String(charArray)); |
| } |
| return board; |
| } |
| } |
3.3、更多
52 - N 皇后 II
37 - 解数独
4、排列问题
46 - 全排列

更多问题
47 - 全排列 II
4.1、方法一
| public class Solution { |
| |
| private static final List<List<Integer>> res = new ArrayList<>(); |
| private static boolean[] used; |
| |
| public static List<List<Integer>> permute(int[] nums) { |
| res.clear(); |
| if (nums == null || nums.length == 0) return res; |
| |
| used = new boolean[nums.length]; |
| Arrays.fill(used, false); |
| |
| generatePermutation(nums, 0, new ArrayList<>()); |
| return res; |
| } |
| |
| |
| |
| |
| |
| private static void generatePermutation(int[] nums, int index, ArrayList<Integer> p) { |
| if (index == nums.length) { |
| res.add(new ArrayList<>(p)); |
| return; |
| } |
| |
| for (int i = 0; i < nums.length; i++) { |
| if (used[i]) continue; |
| |
| p.add(nums[i]); |
| used[i] = true; |
| generatePermutation(nums, index + 1, p); |
| |
| p.remove(p.size() - 1); |
| used[i] = false; |
| } |
| } |
| } |
4.2、方法二
回溯算法核心套路详解
回溯算法解题套路框架
| public class Solution { |
| |
| private static final List<List<Integer>> res = new ArrayList<>(); |
| private static boolean[] used; |
| |
| public static List<List<Integer>> permute(int[] nums) { |
| res.clear(); |
| if (nums == null || nums.length == 0) return res; |
| |
| used = new boolean[nums.length]; |
| |
| LinkedList<Integer> track = new LinkedList<>(); |
| backtrack(nums, track); |
| return res; |
| } |
| |
| |
| |
| |
| private static void backtrack(int[] nums, LinkedList<Integer> track) { |
| |
| if (track.size() == nums.length) { |
| res.add(new LinkedList<>(track)); |
| return; |
| } |
| |
| for (int i = 0; i < nums.length; i++) { |
| if (used[i]) continue; |
| |
| track.add(nums[i]); |
| used[i] = true; |
| backtrack(nums, track); |
| |
| track.removeLast(); |
| used[i] = false; |
| } |
| } |
| } |
5、组合问题
77 - 组合

5.1、方法一
| public class Solution { |
| |
| private static final List<List<Integer>> res = new ArrayList<>(); |
| |
| public static List<List<Integer>> combine(int n, int k) { |
| res.clear(); |
| |
| LinkedList<Integer> track = new LinkedList<>(); |
| generateCombinations(n, k, 1, track); |
| return res; |
| } |
| |
| |
| |
| |
| private static void generateCombinations(int n, int k, int start, LinkedList<Integer> track) { |
| if (track.size() == k) { |
| res.add(new LinkedList<>(track)); |
| return; |
| } |
| |
| for (int i = start; i <= n; i++) { |
| track.add(i); |
| generateCombinations(n, k, i + 1, track); |
| track.removeLast(); |
| } |
| } |
| } |
5.2、方法二
回溯算法秒杀所有排列/组合/子集问题
回溯算法秒杀所有排列-组合-子集问题
| public class Solution { |
| |
| private static final List<List<Integer>> res = new LinkedList<>(); |
| private static final LinkedList<Integer> track = new LinkedList<>(); |
| |
| public static List<List<Integer>> combine(int n, int k) { |
| res.clear(); |
| track.clear(); |
| |
| backtrack(1, n, k); |
| return res; |
| } |
| |
| |
| |
| |
| private static void backtrack(int start, int n, int k) { |
| |
| if (k == track.size()) { |
| res.add(new LinkedList<>(track)); |
| return; |
| } |
| |
| for (int i = start; i <= n; i++) { |
| track.addLast(i); |
| backtrack(i + 1, n, k); |
| track.removeLast(); |
| } |
| } |
| } |
6、组合问题的优化
更多问题
39 - 组合总和
40 - 组合总和 II
216 - 组合总和 III
78 - 子集
90 - 子集 II
401 - 二进制手表
6.1、方法一
| public class Solution { |
| |
| private static final List<List<Integer>> res = new ArrayList<>(); |
| |
| public static List<List<Integer>> combine(int n, int k) { |
| res.clear(); |
| |
| LinkedList<Integer> track = new LinkedList<>(); |
| generateCombinations(n, k, 1, track); |
| return res; |
| } |
| |
| |
| |
| |
| private static void generateCombinations(int n, int k, int start, LinkedList<Integer> track) { |
| if (track.size() == k) { |
| res.add(new LinkedList<>(track)); |
| return; |
| } |
| |
| |
| |
| for (int i = start; i <= n - (k - track.size() - 1); i++) { |
| track.add(i); |
| generateCombinations(n, k, i + 1, track); |
| track.removeLast(); |
| } |
| } |
| } |
6.2、方法二
| public class Solution { |
| |
| private static final List<List<Integer>> res = new ArrayList<>(); |
| private static final LinkedList<Integer> track = new LinkedList<>(); |
| |
| public static List<List<Integer>> combine(int n, int k) { |
| res.clear(); |
| track.clear(); |
| |
| backtrack(1, n, k); |
| return res; |
| } |
| |
| private static void backtrack(int start, int n, int k) { |
| |
| if (k == track.size()) { |
| res.add(new LinkedList<>(track)); |
| return; |
| } |
| |
| |
| |
| for (int i = start; i <= n - (k - track.size() - 1); i++) { |
| track.addLast(i); |
| backtrack(i + 1, n, k); |
| track.removeLast(); |
| } |
| } |
| } |
7、floodfill 算法
200 - 岛屿数量

| public class Solution { |
| |
| private static int dir[][] = { |
| {0, 1}, {1, 0}, {0, -1}, {-1, 0} |
| }; |
| private static int m, n; |
| private static boolean visited[][]; |
| |
| public static int numIslands(char[][] grid) { |
| if (grid == null || grid.length == 0 || grid[0].length == 0) return 0; |
| |
| m = grid.length; |
| n = grid[0].length; |
| visited = new boolean[m][n]; |
| |
| int res = 0; |
| for (int i = 0; i < m; i++) { |
| for (int j = 0; j < n; j++) { |
| if (grid[i][j] == '1' && !visited[i][j]) { |
| res++; |
| dfs(grid, i, j); |
| } |
| } |
| } |
| |
| return res; |
| } |
| |
| |
| |
| |
| |
| private static void dfs(char[][] grid, int x, int y) { |
| visited[x][y] = true; |
| for (int i = 0; i < 4; i++) { |
| int newx = x + dir[i][0]; |
| int newy = y + dir[i][1]; |
| if (inArea(newx, newy) && !visited[newx][newy] && grid[newx][newy] == '1') dfs(grid, newx, newy); |
| } |
| } |
| |
| private static boolean inArea(int x, int y) { |
| return x >= 0 && x < m && y >= 0 && y < n; |
| } |
| } |
更多问题
130 - 被围绕的区域
417 - 太平洋大西洋水流问题
8、二维平面上的回溯法
79 - 单词搜索

| public class Solution { |
| |
| |
| private static int dir[][] = { |
| {-1, 0}, {0, 1}, {1, 0}, {0, -1} |
| }; |
| private static int m, n; |
| private static boolean[][] visited; |
| |
| public static boolean exist(char[][] board, String word) { |
| m = board.length; |
| n = board[0].length; |
| visited = new boolean[m][n]; |
| |
| for (int i = 0; i < m; i++) { |
| for (int j = 0; j < n; j++) { |
| if (searchWord(board, word, 0, i, j)) return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| |
| |
| private static boolean searchWord(char[][] board, String word, int index, int startx, int starty) { |
| if (index == word.length() - 1) return board[startx][starty] == word.charAt(index); |
| |
| if (board[startx][starty] == word.charAt(index)) { |
| visited[startx][starty] = true; |
| |
| for (int i = 0; i < 4; i++) { |
| int newx = startx + dir[i][0]; |
| int newy = starty + dir[i][1]; |
| if (inArea(newx, newy) && !visited[newx][newy] && searchWord(board, word, index + 1, newx, newy)) |
| return true; |
| } |
| visited[startx][starty] = false; |
| } |
| |
| return false; |
| } |
| |
| private static boolean inArea(int x, int y) { |
| return x >= 0 && x < m && y >= 0 && y < n; |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步