1. 程式人生 > >C++ STL求全排列和組合

C++ STL求全排列和組合

C++11 STL內建了求全排列的模板函式next_permutation和prev_permutation,屬於<algorithm>標頭檔案和std名稱空間,使用非常方便。例如:

vector<int> A{1,2,3,4,5};
while (next_permutation(A.begin(),A.end())
{
    cout<<A[0]<<A[1]<<A[2]<<A[3]<<A[4]<<endl;
}

其本質上是通過交換元素實現的,返回值為true則表示排列尚未窮盡。所以輸入陣列初始狀態應按升序排列。

但是組合就沒有這種方便的函式可用了。
例如C(n,m),在m較小時可以手寫迴圈,以C(5,3)為例:

    vector<int> A{1,2,3,4,5};
    for (int i = 0; i < A.size() - 2; ++i)
        for (int j = i + 1; j < A.size() - 1; ++j)
            for (int k = j + 1; k < A.size(); ++k)
            {
                cout<<A[i]<<A[j]<<A[k]<<endl;
            }

但是這樣需要寫m個迴圈函式,我們改成遞迴形式,再改成模板函式,這樣就可以同時支援vector<int>string了:


template <typename T>
void combine_inner(T &data, int start, int n, int m, int depth, T temp,vector<T> &result)
{
    if (depth == m - 1)
    {
        //最內層迴圈 將temp加入result
        for (int i = start; i < n - (m - depth - 1
); ++i) { temp[depth] = data[i]; result.push_back(temp); } } else for (int i = start; i < n - (m - depth - 1);++i) { temp[depth] = data[i];//每層輸出一個元素 combine_inner(data,i + 1, n, m, depth+1,temp,result); } } //T可以調入vector<int>, string等,需要支援下標[]操作及size()函式 template <typename T> vector<T> combine(T &data,int m) { if (m <= 0) return{}; int depth = 0; vector<T> result; T temp(m,0); combine_inner(data,0, data.size(), m, depth,temp,result); return result; }

呼叫時只需要這樣:

    string s("ABCDEF");
    vector<string> result = combine(s, 3);

很方便吧。時間複雜度O(C(n,m)),空間複雜度O(1)。