題目網址:https://leetcode.cn/problems/combination-sum-ii/

題意:給一 array candidates 和一整數 target, 求 candidates 中所有可以使數字和為 target 的組合, 且 candidates 中的每個數字在每個組合中只能使用一次

注意:Output 不能有重複的組合

Solution 1:

想法:先做 sorting, 再用 DFS, 類似 39. Combination Sum, 一旦不取 nums[j], 則後面區間 [j + 1, n) 中若有跟 candidates[j] 相同的都直接跳過

e.g. candidates = [10,1,2,7,6,1,5], target = 8

  • 先做 sorting
  • 若不取 idx = 01, 則可以直接跳過 idx = 11, 讓 idx = 2 的元素開始取

class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
dfs(candidates, 0, candidates.size(), target);
return res;
}

private:
vector<int> cur;
vector<vector<int>> res;

void dfs(vector<int>& candidates, int i, int n, int target){
if (target == 0) {
res.emplace_back(cur);
return;
} else if (target < 0 || i == n) {
return;
}

// 取
cur.emplace_back(candidates[i]);
dfs(candidates, i + 1, n, target - candidates[i]);
cur.pop_back();

// 不取, 若後面有跟 candidates[i] 值一樣的, 則都跳過
while (i + 1 < n && candidates[i] == candidates[i + 1]) {
++i;
}

dfs(candidates, i + 1, n, target);
}
};
  • time:$O(n \cdot 2^n)$ ➔ $O(n \cdot log(n)) + O(n \cdot 2^n)$
    • $O(n \cdot log(n))$:sorting
    • $O(n \cdot 2^n)$:最多有 $2^n$ 種組合, 建構每一種組合需 $O(n)$ time
  • space:$O(n)$ ➔ 若不考慮要返回的 array, 則取決於遞迴的最大深度

Solution 2:

想法:同 Solution 1, 只是透過剪枝(pruning)來做優化, 一旦當前元素超過 target, 則直接退出(因為有排序)。

class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
dfs(candidates, 0, candidates.size(), target);
return res;
}

private:
vector<int> cur;
vector<vector<int>> res;

void dfs(vector<int>& candidates, int i, int n, int target){
if (target == 0) {
res.emplace_back(cur);
return;
}

// 若要取, 則會遞迴執行;若不取, 則 for loop 迭代到 j + 1
for (int j = i; j < n; ++j) {
// 剪枝, 因為有排序, 故 candidates[j] > target, 則後面皆 > target
if (candidates[j] > target) {
break;
}

// 取
cur.emplace_back(candidates[j]);
dfs(candidates, j + 1, n, target - candidates[j]);
cur.pop_back();

// 不取, 若後面有跟 candidates[i] 值一樣的, 則都跳過
while (j + 1 < n && candidates[j] == candidates[j + 1]) {
++j;
}
}
}
};
  • time:$O(n \cdot 2^n)$ ➔ worse case 仍為 $O(n \cdot 2^n)$, 透過使用 pruning, 實際運行情況應該是遠小於 $O(n \cdot 2^n)$ 的
  • space:$O(target)$ ➔ worse case 下, dfs() 最大遞迴深度為 $O(target)$