1. 程式人生 > 實用技巧 >c++ abcd....等等字元所有不同的非重複組合排布

c++ abcd....等等字元所有不同的非重複組合排布

比如abc的組合型別 abc acb bac bca cab cba.

分析: 我們當然可以寫三個巢狀的for迴圈去取,但是如果是abcd的組合呢? 還要修改程式碼新增一層for。 那如果是n個字母呢? 你又要手動新增多少層for呢?

所以需要一個忽視字母個數的實現。我們知道多層for(每次for裡面的處理還類似)那麼可以昇華成遞迴呼叫。這裡我們用遞迴解決。

以abc為例

(第一層遞迴裡) 取a, 給第二層傳bc進去(同時告訴第二層,截止第一層字串組合是a)

(第二層遞迴裡) 取b,給第三層傳c進去(同事告訴第三層,截止第二層字串組合出的是ab)

(第三層遞迴裡) 取c。 發現這一層只有一個c可以取,那麼在第二層字串組合裡面加入c,return。 ---->此時完成一種排列組合: abc

(第一層遞迴裡) 取a, 給第二層傳bc進去(同時告訴第二層,截止第一層字串組合是a)

(第二層遞迴裡) 取c,給第三層傳b進去(同事告訴第三層,截止第二層字串組合出的是ac)

(第三層遞迴裡) 取b。 發現這一層只有一個b可以取,那麼在第二層字串組合裡面加入b,return。 ---->此時完成一種排列組合: acb

(第一層遞迴裡) 取b, 給第二層傳ac進去(同時告訴第二層,截止第一層字串組合是b)

(第二層遞迴裡) 取a,給第三層傳c進去(同事告訴第三層,截止第二層字串組合出的是ba)

(第三層遞迴裡) 取c。 發現這一層只有一個c可以取,那麼在第二層字串組合裡面加入c,return。 ---->此時完成一種排列組合:bac

.........

..........

可以看到上面的設計是可以把所有排列都取到的。 那麼接下來是程式碼實現:

#include<iostream>
#include<list>
#include<vector>
#include<string>
using namespace std;

//最開始變數用的vector<char>,後面發現要刪除元素,vector效率低,list效率高。要修改的話,每一個vector<char>都要改成list<char>好煩啊。這裡統一用個別名,以後就可以只修改這裡就好了。
typedef list<char> AlpContainer; //Alps:這層可用的字母集, strs:用來記錄所有完成的字母組合, preStr:上一層搞出來的字母組合 void getCurAlphabet(const AlpContainer& Alps, vector<string>& strs, const string& preStr) { if (Alps.size() == 1) //最後一層的話,把組合成型的字母組合記錄下來 { AlpContainer::const_iterator it = Alps.begin(); string myCurStr(preStr); myCurStr += string(1,*it); //完成所有排布的字母組合 strs.push_back(myCurStr); return; } for (AlpContainer::const_iterator it = Alps.begin(); it != Alps.end(); ++it) //這層從可用字母集中順序挑一個字母出來 { string myCurStr(preStr); myCurStr += string(1,*it); //到這層為止的字母組合 AlpContainer sonAlps(Alps); //從可用字母中剃掉這層用了的字母 sonAlps.remove(*it); getCurAlphabet(sonAlps, strs, myCurStr); //讓下一層繼續進行剩餘字母的組合工作 } } int main() { AlpContainer objs; objs.push_back('a'); objs.push_back('b'); objs.push_back('c'); objs.push_back('d'); vector<string> result; string preStr; getCurAlphabet(objs, result, preStr); for(vector<string>::size_type i = 0; i < result.size(); ++i) cout << i+1 << " : " << result[i] << endl; }