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);
}
}