1. 程式人生 > >[LeetCode] Factor Combinations 因子組合

[LeetCode] Factor Combinations 因子組合

Numbers can be regarded as product of its factors. For example,

8 = 2 x 2 x 2;
  = 2 x 4.

Write a function that takes an integer n and return all possible combinations of its factors.

Note: 

  1. Each combination's factors must be sorted ascending, for example: The factors of 2 and 6 is [2, 6], not [6, 2]
    .
  2. You may assume that n is always positive.
  3. Factors should be greater than 1 and less than n.

Examples: 
input: 1
output: 

[]

input: 37
output: 

[]

input: 12
output:

[
  [2, 6],
  [2, 2, 3],
  [3, 4]
]

input: 32
output:

[
  [2, 16],
  [2, 2, 8],
  [2, 2, 2, 4],
  [2, 2, 2, 2, 2],
  [2, 4, 4],
  [4, 8]
]

這道題給了我們一個正整數n,讓我們寫出所有的因子相乘的形式,而且規定了因子從小到大的順序排列,那麼對於這種需要列出所有的情況的題目,通常都是用回溯法來求解的,由於題目中說明了1和n本身不能算其因子,那麼我們可以從2開始遍歷到n,如果當前的數i可以被n整除,說明i是n的一個因子,我們將其存入一位陣列out中,然後遞迴呼叫n/i,此時不從2開始遍歷,而是從i遍歷到n/i,停止的條件是當n等於1時,如果此時out中有因子,我們將這個組合存入結果res中,參見程式碼如下:

解法一:

class Solution {
public:
    vector<vector<int
>> getFactors(int n) { vector<vector<int>> res; helper(n, 2, {}, res); return res; } void helper(int n, int start, vector<int> out, vector<vector<int>> &res) { if (n == 1) { if (out.size() > 1) res.push_back(out); } else { for (int i = start; i <= n; ++i) { if (n % i == 0) { out.push_back(i); helper(n / i, i, out, res); out.pop_back(); } } } } };

下面這種方法用了個小trick,我們仔細觀察題目中給的兩個例子的結果,可以發現每個組合的第一個數字都沒有超過n的平方根,這個也很好理解,由於要求序列是從小到大排列的,那麼如果第一個數字大於了n的平方根,而且n本身又不算因子,那麼後面那個因子也必然要與n的平方根,這樣乘起來就必然會超過n,所以不會出現這種情況。那麼我們剛開始在2到n的平方根之間進行遍歷,如果遇到因子,先複製原來的一位陣列out為一個新的一位陣列new_out,然後把此因子i加入new_out,然後再遞迴呼叫n/i,並且從i遍歷到n/i的平方根,之後再把n/i放入new_out,並且存入結果res,由於層層迭代的呼叫,凡是本身能繼續拆分成更小因數的都能在之後的迭代中拆分出來,並且加上之前結果,最終都會存res中,參見程式碼如下:

解法二:

class Solution {
public:
    vector<vector<int>> getFactors(int n) {
        vector<vector<int>> res;
        helper(n, 2, {}, res);
        return res;
    }
    void helper(int n, int start, vector<int> out, vector<vector<int>> &res) {
        for (int i = start; i <= sqrt(n); ++i) {
            if (n % i == 0) {
                vector<int> new_out = out;
                new_out.push_back(i);
                helper(n / i, i, new_out, res);
                new_out.push_back(n / i);
                res.push_back(new_out);
            }
        }
    }
};

上面兩種解法雖有些小不同,但是構成結果的順序都是相同,對於題目中給的兩個例子n = 12和n = 32,結果如下:

n = 12
2 2 3
2 6
3 4

n = 32
2 2 2 2 2
2 2 2 4
2 2 8
2 4 4
2 16
4 8

上面兩種方法得到的結果跟題目中給的答案的順序不同,雖然順序不同,但是並不影響其通過OJ。我們下面就給出生成題目中的順序的解法,這種方法也不難理解,還是從2遍歷到n的平方根,如果i是因子,那麼我們遞迴呼叫n/i,結果用v來儲存,然後我們新建一個包含i和n/i兩個因子的序列out,然後將其存入結果res, 然後我們再遍歷之前遞迴n/i的所得到的序列,如果i小於等於某個序列的第一個數字,那麼我們將其插入該序列的首位置,然後將序列存入結果res中,我們舉個例子,比n = 12,那麼剛開始i = 2,是因子,然後對6呼叫遞迴,得到{2, 3},然後此時將{2, 6}先存入結果中,然後發現i(此時為2)小於等於{2, 3}中的第一個數字2,那麼將2插入首位置得到{2, 2, 3}加入結果,然後此時i變成3,還是因子,對4呼叫遞迴,得到{2, 2},此時先把{3, 4}存入結果,然後發現i(此時為3)大於{2, 2}中的第一個數字2,不做任何處理直接返回,這樣我們就得到正確的結果了:

解法三:

class Solution {
public:
    vector<vector<int>> getFactors(int n) {
        vector<vector<int>> res;
        for (int i = 2; i * i <= n; ++i) {
            if (n % i == 0) {
                vector<vector<int>> v = getFactors(n / i);
                vector<int> out{i, n / i};
                res.push_back(out);
                for (auto a : v) {
                    if (i <= a[0]) {
                        a.insert(a.begin(), i);
                        res.push_back(a);
                    }
                }
            }
        }
        return res;
    }
};

這種方法對於對於題目中給的兩個例子n = 12和n = 32,結果和題目中給的相同:

n = 12
2 6
2 2 3
3 4

n = 32
2 16
2 2 8
2 2 2 4
2 2 2 2 2
2 4 4
4 8

類似題目:

參考資料: