给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = , target = 8, 输出:
|
示例 2:
输入: candidates = , target = 5, 输出:
|
提示:
1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30
思路
本题的思路与组合总数大抵一致,但由于本题存在重复元素,所以要先进行排序,然后剪枝,否则会超时。
如果当前搜索的序列中的前缀存在重复元素,只需将前缀中的重复元素去除即可。
如序列1,1,1,2,3
的搜索结果中一定包含了1,1,2,3
的搜索结果。
class Solution {
public static int[] nums; public static Map<String, List<Integer>> ans; public static List<Integer> tmp;
public List<List<Integer>> combinationSum2(int[] candidates, int target) { this.nums = candidates; this.ans = new HashMap<>(); this.tmp = new ArrayList<>(); sort(0, nums.length); System.out.println(Arrays.toString(nums)); dfs(target, 0); return ans.values().stream().collect(Collectors.toList()); }
public static void sort(int low, int high) { if (high - low < 2) return; swap(low, (int)(low + Math.random() * (high - low - 1))); int i = low , j = high - 1; boolean direction = true; while (i < j) { if (nums[i] > nums[j]) { swap(i, j); direction = !direction; } int tmp = direction ? i++ : j--; } sort(low, i); sort(i+1, high); }
public static void swap(int x, int y) { if (nums[x] == nums[y]) return; nums[x] ^= nums[y]; nums[y] ^= nums[x]; nums[x] ^= nums[y]; }
public static void dfs(int target, int start) { if (target < 0) return;
if (target == 0) { List<Integer> l = new ArrayList<>(tmp); l.sort( new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { return o1 - o2; } } ); ans.put(l.toString(), l); return; }
for (int i = start; i < nums.length; i++) { if (i -1 >= start && nums[i] == nums[i-1]) continue; tmp.add(nums[i]); dfs(target - nums[i], i+1); tmp.remove(tmp.size() - 1); } }
}
|
复杂度
与组合总数一样