1. 求一个集合的全部子集。
思路:原集合中的每个元素有两种选择,要么加入要么不加入,所有元素的选择放在一起可构成了一系列等长向量,所有可能的等长向量构成了解空间。
import java.util.ArrayList; import java.util.List; public class Main{ public static void main(String[] args){ int[] nums = {1,2,3}; Backtrace A = new Backtrace(); List<List<Integer>> res = A.findAllSubSet(nums); System.out.println(res); } } class Backtrace{ boolean[] isExist; List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> findAllSubSet(int[] nums){ isExist = new boolean[nums.length]; backtrace(0, nums); return res; } public void backtrace(int index, int[] nums){ if(index == nums.length){ //如果已经遍历一遍,那么就处理一下当前获得的解 List<Integer> cur = new ArrayList<>(); for(int i = 0; i < nums.length; i++){ if(isExist[i] == true){ cur.add(nums[i]); } } res.add(new ArrayList<>(cur)); } else{ isExist[index] = false; backtrace(index + 1, nums); isExist[index] = true; backtrace(index + 1, nums); } } } /* output: [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]] */
2. 求一个n元集合的k元子集(n>=k>0)
思路:和求n元集合的所有子集类似,用k来限制一下就可以了。
import java.util.ArrayList; import java.util.List; public class Main{ public static void main(String[] args){ int[] nums = {1,2,3,4,5}; int k = 3; Backtrace A = new Backtrace(); List<List<Integer>> res = A.findAllKSet(nums, k); System.out.println(res); } } class Backtrace{ boolean[] isExist; List<List<Integer>> res = new ArrayList<>(); int k; public List<List<Integer>> findAllKSet(int[] nums, int k){ this.k = k; isExist = new boolean[nums.length]; backtrace(0, nums); return res; } public void backtrace(int index, int[] nums){ if(index == nums.length){ //如果已经遍历一遍,那么就处理一下当前获得的解 List<Integer> cur = new ArrayList<>(); int count = 0; for(int i = 0; i < nums.length; i++){ if(isExist[i] == true){ cur.add(nums[i]); count ++; } } if(count == k){ res.add(new ArrayList<>(cur)); } else{ return ; } } else{ isExist[index] = false; backtrace(index + 1, nums); isExist[index] = true; backtrace(index + 1, nums); } } } /* output: [[3, 4, 5], [2, 4, 5], [2, 3, 5], [2, 3, 4], [1, 4, 5], [1, 3, 5], [1, 3, 4], [1, 2, 5], [1, 2, 4], [1, 2, 3]] */
3. 不重复元素的全排列
思路:用used判断数组判断元素是否被使用,通过长度限制得到全排列。
import java.util.ArrayList; import java.util.List; public class Main{ public static void main(String[] args){ int[] nums = {1,2,3}; BackTrace A = new BackTrace(); List<List<Integer>> res = A.permutaion(nums); System.out.println(res); } } class BackTrace { List<List<Integer>> res = new ArrayList<>(); boolean[] used; public List<List<Integer>> permutaion(int[] nums){ if(0 == nums.length){ return res; } used = new boolean[nums.length]; backtrace(nums, 0, new ArrayList<Integer>()); return res; } public void backtrace(int[] nums, int depth, List<Integer> cur){ if(depth == nums.length){ res.add(new ArrayList<>(cur)); return ; } for(int i = 0; i < nums.length; i++){ if(!used[i]){ used[i] = true; cur.add(nums[i]); backtrace(nums, depth + 1, cur); cur.remove(cur.size() - 1); used[i] = false; } } } } /* 不重复元素,求所有全排列 output: [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] */
4. 重复元素的全排列
思路:在上一个题中,对重复的分支进行剪枝即可
import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Main{ public static void main(String[] args){ int[] nums = {1,3,1}; BackTrace A = new BackTrace(); List<List<Integer>> res = A.permutaion(nums); System.out.println(res); } } class BackTrace { List<List<Integer>> res = new ArrayList<>(); boolean[] used; public List<List<Integer>> permutaion(int[] nums){ if(0 == nums.length){ return res; } used = new boolean[nums.length]; Arrays.sort(nums); backtrace(nums, 0, new ArrayList<Integer>()); return res; } public void backtrace(int[] nums, int depth, List<Integer> cur){ if(depth == nums.length){ res.add(new ArrayList<>(cur)); return ; } for(int i = 0; i < nums.length; i++){ if(!used[i]){ //设前面的数为1,该数为1', 如果该数和前面的数是一样的,并且前面的数没有参与(言外之意上一次递归的时候已经参与完了),按照这条路径下去(从1'开始)必然与前面的路径会相同(从1开始) //可以想想解空间树和递归的过程,详细的讲解看: https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/ if(i > 0 && nums[i] == nums[i-1] && !used[i-1]){ continue; } used[i] = true; cur.add(nums[i]); backtrace(nums, depth + 1, cur); cur.remove(cur.size() - 1); used[i] = false; } } } } /* 重复元素,求所有全排列 output: [[1, 1, 3], [1, 3, 1], [3, 1, 1]] */
5. 电话号码对应字符串
思路:将这些字符和数字对应起来,然后简单回溯即可
import java.util.ArrayList; import java.util.List; public class Main{ public static void main(String[] args){ Solution A = new Solution(); String digits = "25"; List<String> res = A.letterCombinations(digits); System.out.println(res); } } class Solution { String[] tel = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; List<String> res = new ArrayList<>(); public List<String> letterCombinations(String digits) { if(0 == digits.length()){ return res; } char[] number = digits.toCharArray(); backtrace(number, 0, new StringBuilder()); return res; } public void backtrace(char[] number, int index, StringBuilder cur){ if(index == number.length){ res.add(new String(cur)); return ; } for(char c : tel[number[index] - '0'].toCharArray()){ cur.append(c); backtrace(number, index + 1, cur); cur.deleteCharAt(cur.length() - 1); } } } /* 电话号码对应字符串 output: [aj, ak, al, bj, bk, bl, cj, ck, cl] https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ */
6. 解数独
思路:在寻找一个解的时候,通过返回boolean可加快递归的返回。
题目链接:https://leetcode-cn.com/problems/sudoku-solver/
class Solution { boolean[][] colCheck = new boolean[9][10]; boolean[][] rowCheck = new boolean[9][10]; boolean[][][] blockCheck = new boolean[3][3][10]; public void solveSudoku(char[][] board) { for(int row = 0; row < board.length; row++){ for(int col = 0; col < board[0].length; col++){ int num = board[row][col] - '0'; if(1 <= num && num <= 9){ rowCheck[row][num] = true; colCheck[col][num] = true; blockCheck[row/3][col/3][num] = true; } } } backtrace(board, rowCheck, colCheck, blockCheck, 0, 0); } //回溯法寻找一个解的时候,可以通过返回boolean,用于加速递归的返回 public boolean backtrace(char[][] board, boolean[][] rowCheck, boolean[][] colCheck,boolean[][][]blockCheck, int row, int col){ if(col == board[0].length){ col = 0; row++; if(row == board.length){ return true; } } if(board[row][col] == '.'){ for(int num = 1; num <= 9; num++){ boolean canUse = !(rowCheck[row][num] || colCheck[col][num] || blockCheck[row/3][col/3][num]); if(canUse){ board[row][col] = (char)(num + '0'); rowCheck[row][num] = true; colCheck[col][num] = true; blockCheck[row/3][col/3][num] = true; //这里返回的boolean加速了递归的返回,不用再向下执行了,而是直接返回了 //如果什么都不返回,上一步递归无法知道是否已经找到了可行解 if(backtrace(board, rowCheck, colCheck, blockCheck, row, col + 1)){ return true; } board[row][col] = '.'; rowCheck[row][num] = false; colCheck[col][num] = false; blockCheck[row/3][col/3][num] = false; } } } else{ return backtrace(board, rowCheck, colCheck,blockCheck, row, col + 1); } return false; } }
参考链接:
https://www.cnblogs.com/wuyuegb2312/p/3273337.html
https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/