1. leetcode39组合数之和
给定一
无重复元素
数组和目标数,数组的每个元素可以重复使用
找到数组中元素所有可能的组合满足其和等于目标数,要求不能有重复的组合。
- 经典的组合问题
- 递归的出口
- 找到一组解后,自然应当返回上一层
- 当前的
nums[i] > target
,也应该直接返回上一层,因为数组排序后的,在这一层再往后找意义不大
- 每个数字可以重复利用,因此传入下一层的位置不变
- 但是这并不意味着每一层都是从
nums[0]
开始,每一层应该从当前位置开始nums[i]
,这样就可以有效避免由于"往前看"
导致的组合数重复 - 代码
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> results = new ArrayList<>();
List<Integer> item = new ArrayList<>();
if (candidates.length == 0){
return results;
}
Arrays.sort(candidates);
combinationSumHelper(candidates, target, 0, item, results);
return results;
}
private static void combinationSumHelper(int[] candidates,
int target,
int startIndex,
List<Integer> item,
List<List<Integer>> results){
if (target == 0){
results.add( new ArrayList<>(item));
return;
}
for (int i=startIndex; i<candidates.length; i++){
//target再减就是负数了 又数组升序 继续在这一层遍历下一个 i 已经没有意义了
// 所以这里直接返回到上一层
if (candidates[i] > target){
return;
}
item.add(candidates[i]);
//因为每一个元素可以重复使用 所以这里传入下一层的位置不变还是 i
// 这里一个很重要的问题在于 每一次递归并不是 从数组的第0位开始的
// 而是从上一次所在位置开始的
// 简单的说 就是不准往前看!
combinationSumHelper02(candidates, target-candidates[i],i,item,results);
item.remove( item.size() - 1);
}
}
- 配合下图理解会更好
2.leetcode40组合数之和2
给定一
含重复元素
的数组和一目标数,数组的元素只能使用一次
求其所有可能的唯一组合,其和等于目标数
- 含重复元素
- 数组的元素只能使用一次
- 所以相比于上题,本地的最大难点就在于
去重
- 这里去重的思路和求子集2的思路完全一致
- 首先排序
- 其次对于重复元素,只能
从左往右
依次添加,不能跳跃
public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> item = new ArrayList<>();
List<List<Integer>> results = new ArrayList<>();
if (candidates.length == 0){
results.add(item);
return results;
}
Arrays.sort(candidates);
helper02(candidates, target, 0, item, results);
return results;
}
private static void helper02(int[] candidates,
int target,
int startIndex,
List<Integer> item,
List<List<Integer>> results){
if ( target == 0){
results.add( new ArrayList<>(item));
return;
}
for (int i=startIndex; i<candidates.length; i++){
// 规定重复元素只能从左到右依次取 去重思路和子集问题完全一样
if ( i!=0 && candidates[i] == candidates[i-1] && i!=startIndex){
continue;
}
if (candidates[i] > target){
return;
}
item.add( candidates[i] );
//题目要求元素 只能用一次 所以i+1
helper02(candidates, target - candidates[i], i+1, item, results);
item.remove( item.size() - 1);
}
}
3.leetcode216组合数之和3
从数字1-9之间挑选k个数,其和等于n,求所有可能的组合
- 和上述两题几乎一样
- 没有重复元素,不需要考虑去重
- 要求必须为
k
个数,所以添加到结果集的时候需要多一个判断条件
public static List<List<Integer>> combinationSum3(int k, int n) {
int[] nums = {1,2,3,4,5,6,7,8,9};
List<Integer> item = new ArrayList<>();
List<List<Integer>> results = new ArrayList<>();
helper(nums, k, n, 0, item, results);
return results;
}
private static void helper(int[] nums,
int k,
int n,
int startIdnex,
List<Integer> item,
List<List<Integer>> results){
if ( n == 0 && item.size() == k){
results.add( new ArrayList<>(item));
return;
}
for (int i = startIdnex; i<nums.length; i++){
if (item.size() > k){
continue;
}
if (nums[i] > n){
return;
}
item.add(nums[i]);
helper02( nums, k, n - nums[i],i+1,item, results);
item.remove( item.size() - 1);
}
}