回溯演算法的模板
阿新 • • 發佈:2021-11-27
模板
1 /** 2 * 回溯演算法的模板 3 * @param 路徑:已走過的路徑,已經選擇到的資料 4 * @param 選擇列表:原始的資料列表,一般是如此int[] nums 5 */ 6 List<List<Integer>> res = new ArrayList<>(); 7 public void trackBack(路徑, 選擇列表) { 8 if () { 9 res.add(new ArrayList<>(路徑)); 10 return; 11 } 12 13 //start每層(從0開始,或者,遞增加1);end的值可以做剪枝,如組合問題,不夠選了直接退出:i < n-(k-tack.size()) + 1 14 for (待選擇的值 : 選擇列表) { 15 if (排查非法的值) { 16 continue; 17 } 18 選擇; 19 trackBack(路徑, 選擇列表); 20 撤銷選擇,回溯; 21 } 22 }
leetcode真題示例:
39. 組合總和
如果要不能重複,模板中不使用for,該使用【選擇/不選擇】的兩個分支。詳見:
1 /** 2 * 39. 組合總和3 * https://leetcode-cn.com/problems/combination-sum/ 4 */ 5 public class Problem39 { 6 7 public static void main(String[] args) { 8 int[] candidates = new int[]{2,3,6,7}; 9 int target = 7; 10 Solution39 solution39 = new Solution39(); 11 System.out.println(solution39.combinationSum(candidates, target));12 } 13 } 14 15 class Solution39 { 16 private List<List<Integer>> res = new ArrayList<>(); 17 public List<List<Integer>> combinationSum(int[] candidates, int target) { 18 trackBack(candidates, target, 0, new ArrayList<>()); 19 return res; 20 } 21 22 private void trackBack(int[] candidates, int target, int idx, List<Integer> track) { 23 if (target < 0 || idx == candidates.length) { 24 return; 25 } 26 if (target == 0) { 27 res.add(new ArrayList<>(track)); 28 return; 29 } 30 // 不選擇當前值的分支 31 trackBack(candidates, target, idx + 1, track); 32 33 // 選擇當前值的分支 34 int current = candidates[idx]; 35 // 根據條件剪枝 36 if (target - current >= 0) { 37 track.add(current); 38 // 值可以重複使用,所以idx不增加,下次選擇繼續使用 39 trackBack(candidates, target - current, idx, track); 40 track.remove(track.size() - 1); 41 } 42 } 43 }
40. 組合總和 II
1 package com.example.demo.leetcode; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.List; 6 7 /** 8 * 40. 組合總和 II 9 * https://leetcode-cn.com/problems/combination-sum-ii/ 10 */ 11 public class Problem40 { 12 public static void main(String[] args) { 13 14 } 15 } 16 17 class Solution40 { 18 private List<List<Integer>> res = new ArrayList<>(); 19 private boolean[] vis; 20 public List<List<Integer>> combinationSum2(int[] candidates, int target) { 21 vis = new boolean[candidates.length]; 22 // 處理重複的值,要先進行排序 23 Arrays.sort(candidates); 24 trackBack(candidates, target, 0, new ArrayList<>()); 25 return res; 26 } 27 28 private void trackBack(int[] candidates, int target, int idx, List<Integer> track) { 29 if (target == 0) { 30 res.add(new ArrayList<>(track)); 31 return; 32 } 33 if (target < 0 || idx == candidates.length) { 34 return; 35 } 36 37 for (int i = idx; i < candidates.length; i ++) { 38 int current = candidates[i]; 39 // 1、已經被使用了,跳過。2、當前值與前一個值相同,如果前一個值沒被選擇,跳過 40 if (vis[i] || (i > 0 && current == candidates[i - 1] && !vis[i - 1])) { 41 continue; 42 } 43 // 根據條件剪枝 44 if (target - current >= 0) { 45 vis[i] = true; 46 track.add(current); 47 // 值可以重複使用,所以idx不增加,下次選擇繼續使用 48 trackBack(candidates, target - current, i + 1, track); 49 track.remove(track.size() - 1); 50 vis[i] = false; 51 } 52 } 53 } 54 }
216. 組合總和 III
1 package com.example.demo.leetcode; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 216. 組合總和 III 8 * https://leetcode-cn.com/problems/combination-sum-iii/ 9 */ 10 public class Problem216 { 11 public static void main(String[] args) { 12 13 } 14 } 15 16 class Solution216 { 17 public List<List<Integer>> combinationSum3(int k, int n) { 18 List<List<Integer>> res = new ArrayList<>(); 19 trackBack(k, n, 1, new ArrayList<>(), res); 20 return res; 21 } 22 23 private void trackBack(int k, int n, int start, List<Integer> track, List<List<Integer>> res) { 24 int min = 1; int max = 9; 25 if (n == 0 && k == track.size()) { 26 res.add(new ArrayList<>(track)); 27 return; 28 } 29 if (n < 0 || start > max) { 30 return; 31 } 32 33 for (int i = start; i <= max; i++) { 34 if (n - i >= 0) { 35 track.add(i); 36 trackBack(k, n - i, i + 1, track, res); 37 track.remove(track.size() - 1); 38 } 39 } 40 } 41 }
78. 子集
1 package com.example.demo.leetcode; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 78. 子集 8 * https://leetcode-cn.com/problems/subsets/ 9 */ 10 public class Problem78 { 11 public static void main(String[] args) { 12 int[] nums = new int[]{1,2,3}; 13 Solution78 solution78 = new Solution78(); 14 System.out.println(solution78.subsets(nums)); 15 } 16 } 17 18 class Solution78 { 19 private List<List<Integer>> res = new ArrayList<>(); 20 public List<List<Integer>> subsets(int[] nums) { 21 r(nums, 0, new ArrayList<>()); 22 return res; 23 } 24 25 private void r(int[] nums, int start, List<Integer> p) { 26 int length = nums.length; 27 if (start >= length) { 28 res.add(new ArrayList<>(p)); 29 return; 30 } 31 32 // 選當前節點 33 p.add(nums[start]); 34 r(nums, start+1, p); 35 p.remove(p.size() - 1); 36 37 // 不選當前節點 38 r(nums, start + 1, p); 39 } 40 }
90. 子集 II
1 package com.example.demo.leetcode; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.List; 6 7 /** 8 * 90. 子集 II 9 * https://leetcode-cn.com/problems/subsets-ii/ 10 */ 11 public class Problem90 { 12 public static void main(String[] args) { 13 14 } 15 } 16 17 class Solution { 18 private boolean[] vis; 19 public List<List<Integer>> subsetsWithDup(int[] nums) { 20 List<List<Integer>> res = new ArrayList<>(); 21 vis = new boolean[nums.length]; 22 // 解決有重複的問題,先排序 23 Arrays.sort(nums); 24 25 trackBack(nums, 0, new ArrayList<>(), res); 26 return res; 27 } 28 29 private void trackBack(int[] nums, int start, List<Integer> track, List<List<Integer>> res) { 30 res.add(new ArrayList<>(track)); 31 int length = nums.length; 32 if (start >= length) { 33 return; 34 } 35 36 for (int i = start; i < length; i++) { 37 // 相同的元素,同一遞迴層級,只選擇第一個元素 38 if (vis[i] || (i >0 && nums[i] == nums[i - 1] && !vis[i - 1])) { 39 continue; 40 } 41 vis[i] = true; 42 track.add(nums[i]); 43 trackBack(nums, i + 1, track, res); 44 track.remove(track.size() - 1); 45 vis[i] = false; 46 } 47 } 48 }
401. 二進位制手錶
1 package com.example.demo.leetcode; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 401. 二進位制手錶 8 * https://leetcode-cn.com/problems/binary-watch/ 9 */ 10 public class Problem401 { 11 public static void main(String[] args) { 12 int turnedOn = 5; 13 Solution401 solution401 = new Solution401(); 14 System.out.println(solution401.readBinaryWatch(turnedOn)); 15 } 16 } 17 18 class Solution401 { 19 public List<String> readBinaryWatch(int turnedOn) { 20 List<String> res = new ArrayList<>(); 21 trackBack(turnedOn, new StringBuilder(), res); 22 return res; 23 } 24 25 private void trackBack(int turnedOn, StringBuilder track, List<String> res) { 26 int remain = 10 - track.length(); 27 // 剩餘的點不夠選了,直接退出 28 if (turnedOn > remain) { 29 return; 30 } 31 if (turnedOn == 0) { 32 // 用個新的物件來補充0 33 StringBuilder newSb = new StringBuilder(track); 34 for (int i = 0; i <remain; i++) { 35 newSb.append("0"); 36 } 37 int upInt = getInt(newSb, 0, 4); 38 int downInt = getInt(newSb, 4, 10); 39 if (upInt <= 11 && downInt <= 59) { 40 res.add(trans(upInt, false) + ":" + trans(downInt, true)); 41 } 42 return; 43 } 44 45 // 選當前點 46 track.append("1"); 47 trackBack(turnedOn - 1, track, res); 48 track = track.deleteCharAt(track.length() - 1); 49 // 不選當前點 50 track.append("0"); 51 trackBack(turnedOn, track, res); 52 track = track.deleteCharAt(track.length() - 1); 53 } 54 55 /** 56 * 格式化兩位數字,根據flag判斷是否補齊0 57 * @param i 數字 58 * @param flag 是否補0 59 * @return 格式化化的數字 60 */ 61 private String trans(int i, boolean flag) { 62 if (flag) { 63 return String.format("%02d", i); 64 } else { 65 return String.format("%d", i); 66 } 67 } 68 69 /** 70 * 從字串中解析出數字 71 * @param track 原字元 72 * @param start 開始位置 73 * @param end 結束位置 74 * @return 數字 75 */ 76 private int getInt(StringBuilder track, int start, int end) { 77 String s = track.substring(start, end).toString(); 78 return Integer.parseInt(s, 2); 79 } 80 }
79. 單詞搜尋
1 package com.example.demo.leetcode; 2 3 import java.util.Objects; 4 5 /** 6 * 79. 單詞搜尋 7 * https://leetcode-cn.com/problems/word-search/ 8 */ 9 public class Problem79 { 10 public static void main(String[] args) { 11 char[][] board = new char[][] {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}}; 12 String word = "ABCB"; 13 Solution79 solution79 = new Solution79(); 14 System.out.println(solution79.exist(board, word)); 15 } 16 } 17 18 class Solution79 { 19 private int m; 20 private int n; 21 private final int[][] directions = new int[][]{{-1, 0},{1, 0},{0, -1},{0, 1}}; 22 23 private boolean[][] vis; 24 25 public boolean exist(char[][] board, String word) { 26 m = board.length; 27 n = board[0].length; 28 vis = new boolean[m][n]; 29 for (int x = 0; x < m; x++) { 30 for (int y = 0; y < n; y++) { 31 // System.out.println("最外 >>>>>>>>>> " + x + " " + y); 32 if (trackBack(board, word, 0, x, y)) { 33 return true; 34 } 35 } 36 } 37 38 return false; 39 } 40 41 private boolean trackBack(char[][] board, String word, int index, int x, int y) { 42 // System.out.println(" >>>>>>>>>> 每次遞迴 >>>>>>>>>> " + x + " " + y); 43 if (index == word.length() - 1) { 44 // System.out.println("----- 最終找到啦"); 45 return board[x][y] == word.charAt(index); 46 } 47 48 if (Objects.equals(board[x][y], word.charAt(index))) { 49 // System.out.println(" >>>>>>>>>> 當前值找到啦,"); 50 vis[x][y] = true; 51 for (int i = 0; i < 4; i++) { 52 // System.out.println("i = " + i); 53 int newX = x + directions[i][0]; 54 int newY = y + directions[i][1]; 55 if (checkArea(newX, newY) && !vis[newX][newY]) { 56 if (trackBack(board, word, index + 1, newX, newY)) { 57 return true; 58 } 59 } 60 } 61 vis[x][y] = false; 62 } 63 64 return false; 65 } 66 67 private boolean checkArea(int x, int y) { 68 return x >= 0 && x < m && y >= 0 && y < n; 69 } 70 }
200. 島嶼數量
1 package com.example.demo.leetcode; 2 3 /** 4 * 200. 島嶼數量 5 * https://leetcode-cn.com/problems/number-of-islands/ 6 */ 7 public class Problem200 { 8 public static void main(String[] args) { 9 // char[][] grid = new char[][] { 10 // {'1','1','1','1','0'}, 11 // {'1','1','0','1','0'}, 12 // {'1','1','0','0','0'}, 13 // {'0','0','0','0','0'} 14 // }; 15 16 char[][] grid = new char[][] { 17 {'1','1','0','0','0'}, 18 {'1','1','0','0','0'}, 19 {'0','0','1','0','0'}, 20 {'0','0','0','1','1'} 21 }; 22 23 Solution200 solution200 = new Solution200(); 24 System.out.println(solution200.numIslands(grid)); 25 } 26 } 27 28 class Solution200 { 29 private int m; 30 private int n; 31 private boolean[][] vis; 32 // 此處上下左右順序無關。如果有順序要求的話,需要注意陣列內容的順序。 33 private int[][] d = new int[][]{{-1, 0},{1, 0},{0, -1},{0, 1}}; 34 public int numIslands(char[][] grid) { 35 m = grid.length; 36 n = grid[0].length; 37 vis = new boolean[m][n]; 38 39 int res = 0; 40 for (int x = 0; x < m; x++) { 41 for (int y = 0; y < n; y++) { 42 // System.out.println("最外層 >>>>>> " + x + " " + y); 43 if (!vis[x][y] && grid[x][y] == '1') { 44 // System.out.println("最外層 >>>>>> 找到一個陸地" + x + " " + y); 45 res ++; 46 dfs(grid, x, y); 47 } 48 } 49 } 50 return res; 51 } 52 53 private void dfs(char[][] grid, int x, int y) { 54 // System.out.println("遞迴 >>>>>> " + x + " " + y); 55 // 保證是島嶼1的,才允許進入遞迴 56 vis[x][y] = true; 57 for (int i = 0; i < 4; i++) { 58 int newX = x + d[i][0]; 59 int newY = y + d[i][1]; 60 // 保證是島嶼1的,才允許進入遞迴 61 if (checkArea(newX, newY) && !vis[newX][newY] && grid[newX][newY] == '1') { 62 dfs(grid, newX, newY); 63 } 64 } 65 } 66 67 private boolean checkArea(int x, int y) { 68 return x >= 0 && x < m && y >= 0 && y < n; 69 } 70 }
具體例子,請參考labuladong的詳細總結:https://mp.weixin.qq.com/s/qT6WgR6Qwn7ayZkI3AineA