DESC:
题目描述
给出一组候选数 C和一个目标数 T,找出候选数中起来和等于 T 的所有组合。
C中的每个数字在一个组合中只能使用一次。注意:
- 题目中所有的数字(包括目标数 T\ T T )都是正整数
- 组合中的数字 (a1,a2,…,ak) 要按非递增排序 (a1≤a2≤…≤ak).
- 结果中不能包含重复的组合
- 组合之间的排序按照索引从小到大依次比较,小的排在前面,如果索引相同的情况下数值相同,则比较下一个索引。
示例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
CODE:
JAVA:
import java.util.*; public class Solution { public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) { ArrayList<ArrayList<Integer>> res = new ArrayList(); if (num == null || num.length == 0) { return res; } Arrays.sort(num); ArrayList<Integer> valList = new ArrayList(); backTrack(res, 0, valList, 0, num, target); return res; } private void backTrack(ArrayList<ArrayList<Integer>> res, int start, ArrayList<Integer> valList, int sum, int[] num, int target){ if (sum == target) { res.add(new ArrayList<>(valList)); return; } if (start >= num.length) { return; } //注意从start开始,不同于abc,bac组合,这里为和 for (int i=start; i< num.length; i++) { if (i>start && num[i] == num[i-1]) { continue; } valList.add(num[i]); sum += num[i]; //剪枝,后面不需要再遍历,肯定都大,因为数组是递增 if (sum > target) { valList.remove(valList.size()-1); break; } backTrack(res, i+1, valList, sum, num, target); sum -= num[i]; valList.remove(valList.size()-1); } } }
NOTES:
- 回溯法,但要注意去重条件,如123,213是不同组合,但和是一样的,且结果要求递增,123,213->123是重复的结果,所以不同于“有重复数字的所有排列”需求条件;
- 迭代中可以剪枝,但结果超过sum,后面的路径就不需要查找了,肯定都大于目标值;
- 迭代中,下一队列要冲上一队列的索引+1开始,避免重复,如【10,20,30,40】,第一队列为20,则下一队列从30开始迭代,就可避免ab,ba这种重复;
- 重复数字问题和“有重复数字的所有排列”解决方法思想一致