難度 medium
描述
給出一組候選數C和一個目標數T,找出候選數中起來和等於T的所有組合。
C中的每個數字在一個組合中只能使用一次。
注意:
- 題目中所有的數字(包括目標數T)都是正整數
- 組合中的數字要按非遞減排序
- 結果中不能包含重復的組合
- 組合之間的排序按照索引從小到大依次比較,小的排在前面,如果索引相同的情況下數值相同,則比較下一個索引。
示例1
輸入:
[100,10,20,70,60,10,50],80
返回值:
[[10,10,60],[10,20,50],[10,70],[20,60]]
說明:
給定的候選數集是[100,10,20,70,60,10,50],目標數是80
解題思路:這道題目不是很難,還是類似於那種全排列的思路,先把數組排好序,然后進入遞歸函數,遞歸函數有幾個參數:一個是結果二維數組res,原數組num,備選數組out,當前需要達到的目的值target,以及下一個索引的位置start。
進入遞歸函數后,首先判斷當前需要達到的目的之target,如果target<0,說明遞歸到當前函數中,已經添加了過多的元素了,直接返回,如果target=0,那說明遞歸到當前元素時,元素個數剛好滿足所有元素之和為目標值這個條件,這時候添加到結果數組,然后返回;否則target>0,說明還有繼續添加的空間,於是就從start開始,把元素放到備選數組里,然后進入下一層的遞歸函數,下一層的遞歸函數有兩個變化的地方:一個是target值,需要變為target-num[i],表示在新的遞歸函數里目標值的變化,另一個就是start這個值,需要變成i+1,表明當前元素已經添加過了,需要添加下一個元素了。在遞歸函數中兩個需要注意的點:一個是剪枝,進入一個遞歸函數首先判斷在當前函數中需要達到的target,如果target小於或等於0,那么說明不用再添加新的元素了,根據不同情況進行返回即可,這就是剪枝;另一個是去重,重復的情況不應該重復出現,比如num=[10,10,20,50],target=80中,可能會出現兩組[10,20,50],因此需要將重復元素去掉,這里我們只要在添加新元素的時候進行判斷,當i>start且num[i]和num[i-1]相等時,直接continue。
代碼:
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
ArrayList<Integer> out = new ArrayList<>();
Arrays.sort(num);
int len = num.length;
helper(res, out, target, num, 0);
return res;
}
public void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> out, int target, int[] num, int start){
if(target<=0){
if(target<0) return;
else{
ArrayList<Integer> t = new ArrayList<>(out);
res.add(t);
return;
}
}
for(int i=start; i<num.length; i++){
if(i>start && num[i]==num[i-1]) continue;
out.add(num[i]);
helper(res, out, target-num[i], num, i+1);
out.remove(out.size()-1);
}
}
}
相似題目:
39. 組合總和
46. 全排列
NC42 有重復項數字的所有排列
參考資料:
https://blog.nowcoder.net/n/cc7605c9ac99436e91baaa25d515cf18?f=comment