给定一个可能包含重复数字的整数集合,请找出所有元素之和等于某个给定值的所有组合。输出中不得包含重复的组合。例如,输入整数集合[2,2,2,4,3,3],元素之和等于8的组合有2个,分别是[2,2,4]和[2,3,3]。
避免重复的组合的方法是当在某一步决定跳过某个值为m的数字时,跳过所有值为m的数字。例如,假设求[2,2,2]的组合,如果在处理第1个2时决定跳过它并跳过所有的2,那么得到的是一个空的组合。如果选择第1个2之后决定跳过第2个2并连带跳过后面的2,那么得到的是组合[2]。如果选择前两个2之后决定跳过第3个2,那么得到的是组合[2,2]。如果3个2都被选择,则得到组合[2,2,2]。采用这个办法就可以避免产生重复的组合,如避免了两种产生重复组合[2,2]的情形,一种情形是跳过第1个2选择后面两个2,另一种情形是跳过中间一个2选择前后两个2。
为了方便跳过后面所有值相同的数字,可以将集合中的所有数字排序,把相同的数字放在一起,这样方便比较数字。当决定跳过某个值的数字时,可以按顺序扫描后面的数字,直到找到不同的值为止。
public class Test {
public static void main(String[] args) {
int[] nums = {2, 2, 2, 4, 3, 3};
List<List<Integer>> result = combinationSum2(nums, 8);
for (List<Integer> item : result) {
System.out.println(item);
}
}
public static List<List<Integer>> combinationSum2(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> result = new LinkedList<>();
LinkedList<Integer> combination = new LinkedList<>();
helper(nums, target, 0, combination, result);
return result;
}
private static void helper(int[] nums, int target, int i, LinkedList<Integer> combination,
List<List<Integer>> result) {
if (target == 0) {
result.add(new LinkedList<>(combination));
}
else if (target > 0 && i < nums.length) {
helper(nums, target, getNext(nums, i), combination, result);
combination.addLast(nums[i]);
helper(nums, target - nums[i], i + 1, combination, result);
combination.removeLast();
}
}
private static int getNext(int[] nums, int index) {
int next = index;
while (next < nums.length && nums[next] == nums[index]) {
next++;
}
return next;
}
}