目录
1.问题描述
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括
target
)都是正整数。 - 解集不能包含重复的组合
示例1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
2.解题思路
一看问题的描述就知道这个题目的基本解法是回溯法,然后注意问题的描述就是candidates 中的数字可以无限制重复被选取。
2.1 算法思路
对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。
回到本题,我们定义递归函数
private void dfs(int []candidates,int index,int targetSum,int sum)
index表示 数组candidates的第 index位,sum表示当前数组数组累加的和,targetsum 表示我们需要的目标和。递归的终止条件为 sum==targetSum 。
List<List<Integer>> res=new ArrayList<>();
List<Integer> ans=new ArrayList<>();
res表示最后存储的解集的集合,然后ans表示当前解集的集合
本题搜索的过程抽象成树形结构如下:
3.源代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
List<List<Integer>> res=new ArrayList<>();
List<Integer> ans=new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target)
{
dfs(candidates,0,target,0);
return res;
}
private void dfs(int []candidates,int index,int targetSum,int sum){
if(sum>targetSum){
return ;
}
if(sum==targetSum){
res.add(new ArrayList<>(ans));
return ;
}
for(int i=index;i<candidates.length;i++ ){
sum=sum+candidates[i];
ans.add(candidates[i]);
dfs(candidates,i,targetSum,sum);
sum=sum-candidates[i];
ans.remove(ans.size() - 1);
}
}
public static void main(String[] args) {
int []nums={2,3,5};
int target=8;
CombinationSum cm=new CombinationSum();
List<List<Integer>> res=new ArrayList<>();
res=cm.combinationSum(nums,target);
System.out.println(res.toString());
}
}
编译器运行通过:
Leetcode 上运行截图:
4.其他组合总和问题
4.1 Leetcode40-组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
分析该问题,当把问题与上面的问题比较的时候,我们发现唯一的区别就是元素不能重复使用。解决问题的基本解法还是回溯法,在上面的代码基础上我们还是要如何解决元素重复的问题。避免重复的核心代码:
for(int i=index;i<candidates.length;i++ ){
if(i>index&&candidates[i] == candidates[i-1]){
continue;
}
这样可以避免相同的情况筛选两次(一次原生For循环,一次是递归)。
还有就是递归调用dfs的时候我们需要修改条件:
dfs(candidates,i+1,targetSum,sum);
这里的i+1可以避免元素的重复使用。当解决这些问题之后我们直接上源码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
List<List<Integer>> res=new ArrayList<>();
List<Integer> ans=new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target)
{
dfs(candidates,0,target,0);
return res;
}
private void dfs(int []candidates,int index,int targetSum,int sum){
if(sum>targetSum){
return ;
}
if(sum==targetSum){
res.add(new ArrayList<>(ans));
return ;
}
for(int i=index;i<candidates.length;i++ ){
if(i>index&&candidates[i] == candidates[i-1]){
continue;
}
sum=sum+candidates[i];
ans.add(candidates[i]);
dfs(candidates,i+1,targetSum,sum);
sum=sum-candidates[i];
ans.remove(ans.size() - 1);
}
}
public static void main(String[] args) {
int []nums={10,1,2,7,6,1,5};
int target=8;
CombinationSum cm=new CombinationSum();
List<List<Integer>> res=new ArrayList<>();
res=cm.combinationSum(nums,target);
System.out.println(res.toString());
}
}
编译器运行结果:
我们发现原来结果集有重复的情况,仔细想想,递归之前排序就可以解决了。源代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
List<List<Integer>> res=new ArrayList<>();
List<Integer> ans=new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target)
{
Arrays.sort(candidates);
dfs(candidates,0,target,0);
return res;
}
private void dfs(int []candidates,int index,int targetSum,int sum){
if(sum>targetSum){
return ;
}
if(sum==targetSum){
res.add(new ArrayList<>(ans));
return ;
}
for(int i=index;i<candidates.length;i++ ){
if(i>index&&candidates[i] == candidates[i-1]){
continue;
}
sum=sum+candidates[i];
ans.add(candidates[i]);
dfs(candidates,i+1,targetSum,sum);
sum=sum-candidates[i];
ans.remove(ans.size() - 1);
}
}
public static void main(String[] args) {
int []nums={10,1,2,7,6,1,5};
int target=8;
CombinationSum cm=new CombinationSum();
List<List<Integer>> res=new ArrayList<>();
res=cm.combinationSum(nums,target);
System.out.println(res.toString());
}
}
编译器运行结果:
运行结果显示结果没有重复的集合。直接提交leetcode。
4.2 Leetcode216-组合总和 III
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解决问题的基本思路还是回溯法。
首先定义递归函数:
void dfs(int k,int index,int targetSum,int sum)
k 代表数组解集的位数,index代表数组解集的元素值。targetSum代表目标和,sum代表当前解集的和。
递归终止条件为:
ans.size()==k&&sum==targetSum
完整代码如下:
import java.util.ArrayList;
import java.util.List;
public class CombinationSum3 {
List<List<Integer>> lists=new ArrayList<>();
List<Integer> ans=new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
List<Integer> list = new ArrayList<>();
dfs(k,1,n,0);
return lists;
}
void dfs(int k,int index,int targetSum,int sum){
if(ans.size()==k){
if(sum==targetSum)
lists.add(new ArrayList<>(ans));
return ;
}
for(int i=index;i<=9;i++)
{
sum = sum + i;
ans.add(i);
dfs(k,i+1,targetSum,sum);
sum=sum-i;
ans.remove(ans.size()-1);//回溯
}
}
public static void main(String[] args){
List<List<Integer>> lists=new ArrayList<>();
int k=3;
int n=9;
CombinationSum3 cm=new CombinationSum3();
lists=cm.combinationSum3(k,n);
System.out.println(lists.toString());
}
}
运行结果:
直接提交Leetcode
惊喜哈哈哈!