40. Combination Sum II
题目:
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.
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- 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]
链接: http://leetcode.com/problems/combination-sum-ii/
题解:
依然是一道DFS + Backtracking题目。 与之前不同的是每个数字只允许使用一次。所以在回溯的循环里当 i > pos时,假如之后又重复的,continue。我们依然使用candidates[i],而且candidates[i + 1]假如等于candidates[i], 会在DFS的下一个阶段被使用到。当然假如不用这个巧妙的条件也可以, 可以在 target == 0的时候判断res中是否contains list,这样的话运行速度会慢不少,但也可以AC.
Time Complexity - O(), Space Complexity - O()。 如何计算复杂度,智商捉急啊...
public class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> res = new ArrayList<>(); if(candidates == null || candidates.length == 0) return res; Arrays.sort(candidates); ArrayList<Integer> list = new ArrayList<>(); dfs(res, list, candidates, target, 0); return res; } private void dfs(List<List<Integer>> res, ArrayList<Integer> list, int[] candidates, int target, int pos) { if(target == 0) { res.add(new ArrayList<Integer>(list)); return; } if(pos >= candidates.length || target < 0) return; for(int i = pos; i < candidates.length; i++) { if(i > pos && candidates[i] == candidates[i - 1]) //for i > pos, if duplicate,continue. we still use candidates[i] continue; list.add(candidates[i]); dfs(res, list, candidates, target - candidates[i], i + 1); list.remove(list.size() - 1); } } }
二刷:
这里依然是用了跟上一题目很接近的方法。不同的地方在于,每个数字不可以被无限次。所以一个数只能一次,而且遇到重复数字我们要跳过。这样我们在for循环里要加入一条 - if (i > pos && candidates[i] == candidates[i - 1]) continue; 并且在DFS的时候每次
每次新的position = i + 1, 并不是上一题的position = i。
看到有discuss里有方法用array来做backtracking,速度beat 99%,以后也可以把list改成array,试一试这种方法。
Java:
public class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> res = new ArrayList<>(); if (candidates == null || candidates.length == 0) { return res; } Arrays.sort(candidates); List<Integer> comb = new ArrayList<>(); combinationSum2(res, comb, candidates, target, 0); return res; } private void combinationSum2(List<List<Integer>> res, List<Integer> comb, int[] candidates, int target, int pos) { if (target < 0) { return; } else if (target == 0) { res.add(new ArrayList<>(comb)); } for (int i = pos; i < candidates.length; i++) { if (i > pos && candidates[i] == candidates[i - 1]) { continue; } int num = candidates[i]; if (num > target) { return; } comb.add(num); combinationSum2(res, comb, candidates, target - num, i + 1); comb.remove(comb.size() - 1); } } }
三刷:
跟上题唯一不同就是递归时把控制position的变量从 i 变成了 i + 1,这样我们就不会对一个元素进行多次计算。
Java:
public class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> res = new ArrayList<>(); if (candidates == null) return res; Arrays.sort(candidates); findCombinations(res, new ArrayList<>(), candidates, target, 0); return res; } private void findCombinations(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int pos) { if (target < 0) return; if (target == 0) { res.add(new ArrayList<>(list)); return; } for (int i = pos; i < candidates.length; i++) { if (candidates[i] > target) break; if (i > pos && candidates[i] == candidates[i - 1]) continue; list.add(candidates[i]); findCombinations(res, list, candidates, target - candidates[i], i + 1); list.remove(list.size() - 1); } } }
Reference:
https://leetcode.com/submissions/detail/51501884/