1. 程式人生 > 其它 >回溯演算法的模板

回溯演算法的模板

模板

 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