1. 选取 k 个数
回溯法:以选取的个数作为条件跳出
class Solution {
public static void backtracking(List<Integer> combineList, List<List<Integer>> combinations, int start, int k, int n) {
if (k == 0) {
combinations.add(new ArrayList<>(combineList));
return;
}
for (int i = start; i <= n - k + 1; i++) { // 剪枝
combineList.add(i);
backtracking(combineList, combinations, i + 1, k - 1, n);
combineList.remove(combineList.size() - 1);
}
}
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> combinations = new ArrayList<>();
List<Integer> combineList = new ArrayList<>();
backtracking(combineList, combinations, 1, k, n);
return combinations;
}
}
2. 选取固定和 target,数字可以重复选取但解集不含重复组合
回溯法:以和为条件跳出
class Solution {
public static void dfs(int[] candidates,int target,List<Integer> perm,int index,List<List<Integer>> res) {
if(target==0) {
res.add(new ArrayList<Integer>(perm));
return;
}
for(int i=index;i<candidates.length;i++) {
if(target>=candidates[i]) {
perm.add(candidates[i]);
dfs(candidates,target-candidates[i],perm,i,res);
perm.remove(perm.size()-1);
}
}
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<Integer> perm=new ArrayList<Integer>();
dfs(candidates,target,perm,0,res);
return res;
}
}
3. 选取固定和 target,数字不可以重复选取且解集不含重复组合
注意去重
class Solution {
public static void backtracking(List<Integer> tempCombination, List<List<Integer>> combinations,
boolean[] hasVisited, int start, int target, final int[] candidates) {
if (target == 0) {
combinations.add(new ArrayList<>(tempCombination));
return;
}
for (int i = start; i < candidates.length; i++) {
// 第一次进入函数时i=0的时候不会执行到i-1
if (i != 0 && candidates[i] == candidates[i - 1] && !hasVisited[i - 1]) { // 去重的关键
continue;
}
if (candidates[i] <= target) {
tempCombination.add(candidates[i]);
hasVisited[i] = true;
backtracking(tempCombination, combinations, hasVisited, i + 1, target - candidates[i], candidates);
hasVisited[i] = false;
tempCombination.remove(tempCombination.size() - 1);
}else {
return;
}
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> combinations = new ArrayList<>();
Arrays.sort(candidates);
backtracking(new ArrayList<>(), combinations, new boolean[candidates.length], 0, target, candidates);
return combinations;
}
}
4. 同时要求固定和 target 和选取个数 k
回溯法:同时将两个条件用作跳出判断
class Solution {
public static void dfs(List<Integer> perm,int k,int n,int index,List<List<Integer>> res) {
if(n==0 && k==0) {
res.add(new ArrayList<Integer>(perm));
return;
}
if(index<=n) {
for(int st=index;st<=9;st++) {
perm.add(st);
dfs(perm,k-1,n-st,st+1,res);
perm.remove(perm.size()-1);
}
}
}
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
if(n<1 || k==0) {
return res;
}
dfs(new ArrayList<Integer>(),k,n,1,res);
return res;
}
}
5. 选取固定和 target,数字可以重复选取且解集可包含重复的组合(顺序不同)
动态规划:
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 1; i <= target; i++) {
for (int num : nums) {
if (i >= num) {
dp[i] += dp[i - num];
}
}
}
return dp[target];
}
}