组合问题
力扣相关题目:
77.组合
216.组合总和III
39. 组合总和(hot100)
40.组合总和II
17.电话号码的字母组合(hot100)
递归遍历(Java代码):
class Solution {
// 77.组合
// 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合
List<List<Integer>> res;
List<Integer> path;
public void backtracking(int n, int k, int index)
{
if (path.size() == k)
{
res.add(new ArrayList<>(path));
return;
}
// 剪枝
for (int i = index; i <= n - (k - path.size()) + 1; i ++)
{
path.add(i);
backtracking(n,k,i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> combine(int n, int k) {
res = new ArrayList<>();
path = new ArrayList<>();
backtracking(n,k,1);
return res;
}
// 216.组合总和III
// 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int n, int k, int sum, int index)
{
if (sum > n) return;
if (path.size() == k)
{
if (sum == n) res.add(new ArrayList<>(path));
return;
}
for (int i = index; i <= 9 - (k - path.size()) + 1; i ++)
{
path.add(i);
sum += i;
backtracking(n,k,sum,i + 1);
path.remove(path.size() - 1);
sum -= i;
}
}
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(n,k,0,1);
return res;
}
// 39. 组合总和
// 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] candidates, int target,int sum,int index)
{
if (target == sum)
{
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < candidates.length; i ++)
{
if (sum + candidates[i] > target) break;
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates,target,sum,i);
path.remove(path.size() - 1);
sum -= candidates[i];
}
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return res;
}
// 40.组合总和II
// 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] candidates, int target, int sum, int index)
{
if (sum == target)
{
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < candidates.length; i ++)
{
if (sum + candidates[i] > target) break;
if (i > index && candidates[i - 1] == candidates[i]) continue;
path.add(candidates[i]);
sum += candidates[i];
// 和39.组合总和的区别1:这里是i+1,每个数字在每个组合中只能使用一次
backtracking(candidates,target,sum,i + 1);
path.remove(path.size() - 1);
sum -= candidates[i];
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return res;
}
// 17.电话号码的字母组合
// 本题每一个数字代表的是不同集合,也就是求不同集合之间的组合
List<String> res = new ArrayList<>();
StringBuffer tmp = new StringBuffer();
public void backtracking(String digits, String[] numbers, int index)
{
if (digits.length() == index)
{
res.add(tmp.toString());
return;
}
String digit = numbers[digits.charAt(index) - '0'];
for (int i = 0; i < digit.length(); i ++)
{
tmp.append(digit.charAt(i));
backtracking(digits,numbers,index + 1);
tmp.deleteCharAt(tmp.length() - 1);
}
}
public List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0) return res;
String[] numbers = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
backtracking(digits,numbers,0);
return res;
}
}
切割问题
力扣相关题目:
131.分割回文串(hot100)
93.复原IP地址
递归遍历(Java代码):
class Solution {
// 131.分割回文串
// 切割问题类似组合问题
List<List<String>> res = new ArrayList<>();
List<String> path = new ArrayList<>();
public boolean isPalindrome(String s, int start, int end)
{
for (int i = start, j = end; i < j; i ++, j --)
{
if (s.charAt(i) != s.charAt(j)) return false;
}
return true;
}
// index表示切割线
public void backtracking(String s, int index)
{
if (index >= s.length())
{
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < s.length(); i ++)
{
if (isPalindrome(s,index,i))
{
path.add(s.substring(index,i + 1));
}
else continue;
backtracking(s,i + 1);
path.remove(path.size() - 1);
}
}
public List<List<String>> partition(String s) {
backtracking(s,0);
return res;
}
// 93.复原IP地址
List<String> res = new ArrayList<>();
public void backtracking(StringBuilder s, int index, int dot)
{
if (dot == 3)
{
if (valid(s,index,s.length() - 1)) res.add(s.toString());
return;
}
for (int i = index; i < s.length(); i ++)
{
if (valid(s,index,i))
{
s.insert(i + 1, '.');
backtracking(s, i + 2, dot + 1);
s.deleteCharAt(i + 1);
}
else break;
}
}
public boolean valid(StringBuilder s, int start, int end)
{
if (start > end) return false;
if (s.charAt(start) == '0' && start != end) return false;
int num = 0;
for (int i = start; i <= end; i ++)
{
if (s.charAt(i) > '9' || s.charAt(i) < '0') return false;
num = num*10 + (s.charAt(i) - '0');
if (num > 255) return false;
}
return true;
}
public List<String> restoreIpAddresses(String s) {
StringBuilder sb = new StringBuilder(s);
backtracking(sb, 0, 0);
return res;
}
}
子集问题
力扣相关题目:
78.子集(hot100)
90.子集II
491.递增子序列
递归遍历(Java代码):
class Solution {
// 78.子集
// 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] nums, int index)
{
res.add(new ArrayList<>(path));
if (index >= nums.length) return;
for (int i = index; i < nums.length; i ++)
{
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return res;
}
// 90.子集II
// 这道题目和78.子集 (opens new window)区别就是集合里有重复元素了,而且求取的子集要去重
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] nums, int index)
{
res.add(new ArrayList<>(path));
if (index >= nums.length) return;
for (int i = index; i < nums.length; i ++)
{
if (i > index && nums[i] == nums[i - 1]) continue;
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backtracking(nums,0);
return res;
}
// 491.递增子序列
// 不能进行排序去重,所以使用集合的方法
// 但子集问题一定要排序
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] nums, int index)
{
if (path.size() >= 2)
{
res.add(new ArrayList<>(path));
}
Set<Integer> set = new HashSet<>();
for (int i = index; i < nums.length; i ++)
{
if (!path.isEmpty() && path.get(path.size() - 1) > nums[i] || set.contains(nums[i])) continue;
set.add(nums[i]);
path.add(nums[i]);
backtracking(nums,i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums,0);
return res;
}
}
排列问题
力扣相关题目:
46.全排列(hot100)
47.全排列 II
491.递增子序列
递归遍历(Java代码):
class Solution {
// 46.全排列
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public void backtracking(int[] nums)
{
if (path.size() == nums.length)
{
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i ++)
{
if (used[i]) continue;
used[i] = true;
path.add(nums[i]);
backtracking(nums);
path.remove(path.size() - 1);
used[i] = false;
}
}
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0) return res;
used = new boolean[nums.length];
backtracking(nums);
return res;
}
// 47.全排列 II
// 可包含重复数字的序列,所以要排序去重
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public void backtracing(int[] nums)
{
if (path.size() == nums.length)
{
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i ++)
{
if (i >0 && nums[i - 1] == nums[i] && used[i - 1] == false) continue;
if (used[i] != false) continue;
used[i] = true;
path.add(nums[i]);
backtracing(nums);
path.remove(path.size() - 1);
used[i] = false;
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
if (nums.length == 0) return res;
used = new boolean[nums.length];
Arrays.sort(nums);
backtracing(nums);
return res;
}
}
图论额外拓展
力扣相关题目:
332.重新安排行程
递归遍历(Java代码):
class Solution {
// 332.重新安排行程
private Deque<String> res;
private Map<String, Map<String, Integer>> map;
private boolean backTracking(int ticketNum){
if(res.size() == ticketNum + 1){
return true;
}
String last = res.getLast();
if(map.containsKey(last)){//防止出现null
for(Map.Entry<String, Integer> target : map.get(last).entrySet()){
int count = target.getValue();
if(count > 0){
res.add(target.getKey());
target.setValue(count - 1);
if(backTracking(ticketNum)) return true;
res.removeLast();
target.setValue(count);
}
}
}
return false;
}
public List<String> findItinerary(List<List<String>> tickets) {
map = new HashMap<String, Map<String, Integer>>();
res = new LinkedList<>();
for(List<String> t : tickets){
Map<String, Integer> temp;
if(map.containsKey(t.get(0))){
temp = map.get(t.get(0));
temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
}else{
temp = new TreeMap<>();//升序Map
temp.put(t.get(1), 1);
}
map.put(t.get(0), temp);
}
res.add("JFK");
backTracking(tickets.size());
return new ArrayList<>(res);
}
}
棋盘问题
力扣相关题目:
51. N皇后(hot100)
递归遍历(Java代码):
class Solution {
// 51. N皇后
List<List<String>> res = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] chessboard = new char[n][n];
for (char[] c : chessboard) {
Arrays.fill(c, '.');
}
backTrack(n, 0, chessboard);
return res;
}
public void backTrack(int n, int row, char[][] chessboard) {
if (row == n) {
res.add(Array2List(chessboard));
return;
}
for (int col = 0;col < n; ++col) {
if (isValid (row, col, n, chessboard)) {
chessboard[row][col] = 'Q';
backTrack(n, row+1, chessboard);
chessboard[row][col] = '.';
}
}
}
public List Array2List(char[][] chessboard) {
List<String> list = new ArrayList<>();
for (char[] c : chessboard) {
list.add(String.copyValueOf(c));
}
return list;
}
public boolean isValid(int row, int col, int n, char[][] chessboard) {
// 检查列
for (int i=0; i<row; ++i) { // 相当于剪枝
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查45度对角线
for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查135度对角线
for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
}
hot100其他问题
递归遍历(Java代码):
class Solution {
public void dfs_generateParenthesis(List<String> ans, StringBuffer curr, int open, int close, int max)
{
if (curr.length() == max*2)
{
ans.add(curr.toString());
return;
}
if (open < max)
{
curr.append('(');
dfs_generateParenthesis(ans, curr, open + 1, close, max);
curr.deleteCharAt(curr.length() - 1);
}
if (close < open)
{
curr.append(')');
dfs_generateParenthesis(ans, curr, open, close + 1, max);
curr.deleteCharAt(curr.length() - 1);
}
}
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
dfs_generateParenthesis(ans,new StringBuffer(),0,0,n);
return ans;
}
}
递归遍历(Java代码):
class Solution {
boolean[][] visited;
public boolean traverse(char[][] board, String word, int i, int j, int index) {
if (board[i][j] != word.charAt(index)) {
return false;
}
if (index == word.length() - 1) {
return true;
}
int[][] directions = {{0,1}, {0,-1}, {1,0}, {-1,0}};
visited[i][j] = true;
boolean result = false;
for (int[] direction : directions) {
int newi = i + direction[0], newj = j + direction[1];
if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
if (! visited[newi][newj]) {
boolean flag = traverse(board, word, newi, newj, index + 1);
if (flag) {
result = true;
}
}
}
}
visited[i][j] = false;
return result;
}
public boolean exist(char[][] board, String word) {
int m = board.length, n = board[0].length;
visited = new boolean[m][n];
boolean result = false;
for (int i = 0; i < m; i ++) {
for (int j = 0; j < n; j ++) {
boolean flag = traverse(board, word, i, j, 0);
if (flag) {
return flag;
}
}
}
return result;
}
}
回溯问题总结
- 组合问题:N个数里面按一定规则找出k个数的集合
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 棋盘问题:N皇后,解数独等等