Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
JAVA
方法一
与0039题相似,这里还是采用递归的方式,只不过由于输入的数组中存在重复元素,而要求的结果中不允许有重复的结果集合,因此需要向对输入的数组排序,然后通过移动下标跳过重复的元素。
最初只考虑的不从同一个元素开始,所以只在第一次进入循环的时候跳过了重复元素。在实际运行过程中发现,当输入数组是{2,2,2,2},target=4的时候,依然会出现多个重复的结果{2,2}。这是由于虽然由于起始不重复的限制,进入递归的初始下标为0,下标1,2,3均不会作为初始值进入递归,但是在递归内部,会出现下标为{0,1}{0,2}{0,3}的组合,由于下标1、2、3对应的值是相同的,所以最终形成的结果是重复的。
因此需要在递归的过程中,也加入重复值的判断,但是还要保证可以出现相同的数字构成target,例如上例中的结果就是{2,2},两个元素是相同的。一开始没想出来如何避免重复的同时又允许重复数字出现,在经过3个星期的加班之后,忽然发现有办法实现:当每次递归结束返回上一层时,上一层来判断进入下一层递归的起始位置。本质上还是要控制递归的起始下标来跳过重复元素。但是由于下标在最开始的时候回指向重复数字中的第一个,该位置后面的值都是可用的,因此可以满足使用重复的数字构成target。当起始下标后移的时候跳过重复元素,即可实现最终的结果中不存在重复集合的目的。
当输入集合为{1,2,2,2,3,4,7},target=5的时候,第一层递归的起始下标为0,第二层递归的起始下标为1,第三层递归的起始下标为2,此时对应的元素和=target,所以将下标为{0,1,2}的数字加入结果集合中。然后第三层递归结束,返回上一层,第二层递归的下标加1等于2,此时由于NUMS[2]==NUMS[1],因此跳过,下标再加1变为3,此时仍然NUMS[3]==NUMS[2],再次跳过,下标再加1变为4,以此类推。
效率排在前1/5。
public class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new LinkedList<List<Integer>>();
LinkedList<Integer> tempResult = new LinkedList<Integer>();
if(candidates.length != 0){
Arrays.sort(candidates);
for (int i = 0; i < candidates.length; i++) {
if (i != 0 && candidates[i] == candidates[i-1]){
continue;
}
getResult(candidates,target,result,i,0,tempResult);
}
}
return result;
}
public void getResult(int[] candidates, int target,List<List<Integer>> result,
int currentIndex,int currentSum,LinkedList<Integer> tempResult){
currentSum += candidates[currentIndex];
tempResult.add(candidates[currentIndex]);
if(currentSum == target){
result.add((LinkedList)tempResult.clone());
}
while(currentIndex+1 < candidates.length && currentSum + candidates[currentIndex] <= target){
getResult(candidates,target,result,currentIndex+1,currentSum,tempResult);
++currentIndex;
while(currentIndex < candidates.length - 1 && candidates[currentIndex] == candidates[currentIndex + 1]){
++currentIndex;
}
}
tempResult.removeLast();
}
}