1. 程式人生 > >[LeetCode] Combination Sum 組合之和

[LeetCode] Combination Sum 組合之和

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

The same repeated number may be chosen from candidates unlimited number of times.

Note:

  • All numbers (including target
    ) will be positive integers.
  • The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
  [7],
  [2,2,3]
]

Example 2:

Input: candidates = [2,3,5], target = 8,
A solution set is:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

像這種結果要求返回所有符合要求解的題十有八九都是要利用到遞迴,而且解題的思路都大同小異,相類似的題目有 Path Sum IISubsets IIPermutationsPermutations IICombinations 等等,如果仔細研究這些題目發現都是一個套路,都是需要另寫一個遞迴函式,這裡我們新加入三個變數,start記錄當前的遞迴到的下標,out為一個解,res儲存所有已經得到的解,每次呼叫新的遞迴函式時,此時的target要減去當前陣列的的數,具體看程式碼如下:

解法一:

class Solution {
public:
    vector
<vector<int>> combinationSum(vector<int> &candidates, int target) { vector<vector<int>> res; sort(candidates.begin(), candidates.end()); combinationSumDFS(candidates, target, 0, {}, res); return res; } void combinationSumDFS(vector<int> &candidates, int target, int start, vector<int> out, vector<vector<int>> &res) { if (target < 0) return; else if (target == 0) {res.push_back(out); return;} for (int i = start; i < candidates.size(); ++i) { out.push_back(candidates[i]); combinationSumDFS(candidates, target - candidates[i], i, out, res); out.pop_back(); } } };

我們也可以不使用額外的函式,就在一個函式中完成遞迴,還是要先給陣列排序,然後遍歷,如果當前數字大於target,說明肯定無法組成target,由於排過序,之後的也無法組成target,直接break掉。如果當前數字正好等於target,那麼當前單個數字就是一個解,組成一個數組然後放到結果res中。然後我們將當前位置之後的陣列取出來,呼叫遞迴函式,注意此時的target要減去當前的數字,然後我們遍歷遞迴結果返回的二維陣列,將當前數字加到每一個數組最前面,然後再將每個陣列加入結果res即可,參見程式碼如下:

解法二:

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
        vector<vector<int>> res;
        sort(candidates.begin(), candidates.end());
        for (int i = 0; i < candidates.size(); ++i) {
            if (candidates[i] > target) break;
            else if (candidates[i] == target) {res.push_back({candidates[i]}); continue;}
            vector<int> vec = vector<int>(candidates.begin() + i, candidates.end());
            vector<vector<int>> tmp = combinationSum(vec, target - candidates[i]);
            for (auto a : tmp) {
                a.insert(a.begin(), candidates[i]);
                res.push_back(a);
            }
        }
        return res;
    }
};

我們也可以用迭代的解法來做,建立一個三維陣列dp,這裡dp[i]表示目標數為i的所有解法集合。這裡的i就從1遍歷到target即可,對於每個i,我們都新建一個二維陣列cur,然後遍歷candidates陣列,如果遍歷到的數字大於i,說明當前及之後的數字都無法組成i,直接break掉。否則如果相等,那麼把當前數字自己組成一個數組,並且加到cur中。否則就遍歷dp[i - candidates[j] - 1] 中的所有陣列,如果當前數字大於陣列的首元素,則跳過,因為我們的結果要求是要有序的。否則就將當前數字加入陣列的開頭,並且將陣列放入cur之中即可,參見程式碼如下:

解法三:

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
        vector<vector<vector<int>>> dp;
        sort(candidates.begin(), candidates.end());
        for (int i = 1; i <= target; ++i) {
            vector<vector<int>> cur;
            for (int j = 0; j < candidates.size(); ++j) {
                if (candidates[j] > i) break;
                else if (candidates[j] == i) {cur.push_back({candidates[j]}); continue;}
                for (auto a : dp[i - candidates[j] - 1]) {
                    if (candidates[j] > a[0]) continue;
                    a.insert(a.begin(), candidates[j]);
                    cur.push_back(a);
                }
            }
            dp.push_back(cur);
        }
        return dp[target - 1];
    }
};

類似題目:

參考資料: