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/

posted @ 2015-04-17 23:19  YRB  阅读(456)  评论(0编辑  收藏  举报