詳解全排列

2021-08-18 23:31:32 字數 3123 閱讀 1586

題目:用c++寫乙個函式, 如 foo(const char *str), 列印出 str 的全排列, 如 abc 的全排列: abc, acb, bca, bac, cab, cba。。

以下內容參考:

一、全排列的遞迴實現

為方便起見,用123來示例下。123的全排列有123、132、213、231、312、321這六種。首先考慮213和321這二個數是如何得出的。顯然這二個都是123中的1與後面兩數交換得到的。然後可以將123的第二個數和每三個數交換得到132。同理可以根據213和321來得231和312。因此可以知道——全排列就是從第乙個數字起每個數分別與它後面的數字交換。找到這個規律後,遞迴的**就很容易寫出來了:

#includeusing namespace std;

void swap(char *a,char *b)

void allrange(char *pszstr,int k,int m)

else

}}void foo(char *pszstr)

int main()

執行結果如下圖所示:

注意這樣的方法沒有考慮到重複數字,如122將會輸出:

這種輸出絕對不符合要求,因此現在要想辦法來去掉重複的數列。

二、去掉重複數字的全排列遞迴實現

由於全排列就是從第乙個數字起每個數分別與它後面的數字交換。我們先嘗試加個這樣的判斷——如果乙個數與後面的數字相同那麼這二個數就不交換了。如122,第乙個數與後面交換得212、221。然後122中第二數就不用與第三個數交換了,但對212,它第二個數與第三個數是不相同的,交換之後得到221。與由122中第乙個數與第三個數交換所得的221重複了。所以這個方法不行。

換種思維,對122,第乙個數1與第二個數2交換得到212,然後考慮第乙個數1與第三個數2交換,此時由於第三個數等於第二個數,所以第乙個數不再與第三個數交換。再考慮212,它的第二個數與第三個數交換可以得到解決221。此時全排列生成完畢。

這樣我們也得到了在全排列中去掉重複的規則——去重的全排列就是從第乙個數字起每個數分別與它後面非重複出現的數字交換。用程式設計的話描述就是第i個數與第j個數交換時,要求[i,j)中沒有與第j個數相等的數。下面給出完整**:

#includeusing namespace std;

void swap(char *a,char *b)

//在pszstr陣列中,[nbegin,nend)中是否有數字與下標為nend的數字相等

bool isswap(char *pszstr, int nbegin, int nend)

//k表示當前選取到第幾個數,m表示共有多少數.

ok,到現在我們已經能熟練寫出遞迴的方法了,並且考慮了字串中的重複資料可能引發的重複數列問題。那麼如何使用非遞迴的方法來得到全排列了?

三、全排列的費遞迴實現

要考慮全排列的非遞迴實現,先來考慮如何計算字串的下乙個排列。如"1234"的下乙個排列就是"1243"。只要對字串反覆求出下乙個排列,全排列的也就迎刃而解了。

如何計算字串的下乙個排列了?來考慮"926520"這個字串,我們從後向前找第一雙相鄰的遞增數字,"20"、"52"都是非遞增的,"26 "即滿足要求,稱前乙個數字2為替換數,替換數的下標稱為替換點,再從後面找乙個比替換數大的最小數(這個數必然存在),0、2都不行,5可以,將5和2交換得到"956220",然後再將替換點後的字串"6220"顛倒即得到"950226"。

對於像"4321"這種已經是最「大」的排列,採用stl中的處理方法,將字串整個顛倒得到最「小」的排列"1234"並返回false。

這樣,只要乙個迴圈再加上計算字串下乙個排列的函式就可以輕鬆的實現非遞迴的全排列演算法。按上面思路並參考stl中的實現原始碼,不難寫成乙份質量較高的**。值得注意的是在迴圈前要對字串排序下,可以自己寫快速排序的**(請參閱《白話經典演算法之六 快速排序 快速搞定》),也可以直接使用vc庫中的快速排序函式(請參閱《使用vc庫函式中的快速排序函式》)。下面列出完整**:

//全排列的非遞迴實現

#include #include #include void swap(char *a, char *b)

//反轉區間

void reverse(char *a, char *b)

//下乙個排列

bool next_permutation(char a)

}reverse(p, pend);//如果沒有下乙個排列,全部反轉後返回true

至此我們已經運用了遞迴與非遞迴的方法解決了全排列問題,總結一下就是:

1.全排列就是從第乙個數字起每個數分別與它後面的數字交換。

2.去重的全排列就是從第乙個數字起每個數分別與它後面非重複出現的數字交換。

3.全排列的非遞迴就是由後向前找替換數和替換點,然後由後向前找第乙個比替換數大的數與替換數交換,最後顛倒替換點後的所有資料

詳解全排列

題目 用c 寫乙個函式,如 foo const char str 列印出 str 的全排列,如 abc 的全排列 abc,acb,bca,dac,cab,cba。一 全排列的遞迴實現 為方便起見,用123來示例下。123的全排列有123 132 213 231 312 321這六種。首先考慮213和...

全排列演算法(詳解)

運用遞迴的思路,第一位具有n種情況,接著在第一位確定後,第二位有n 1種情況,再接著第二位確定後,第三位有n 2種情況等 例 輸入1 2 3。輸出1 2 3。1 3 2。2 1 3。2 3 1。3 1 2。3 2 1。規律 有n個數的條件下,每個數都會在第一位 n 1 次。這裡用乙個for迴圈就行。...

Java 全排列 遞迴 詳解

一 比如對1 2 3 全排列 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 3 1 2 以遞迴的思想來看 1.對於0號位置元素,分別是1 2 3 實現方法 將原有的0號元素分別於0,1,2號元素交換。2.除0號位置元素外,對剩下的子串行進行全排列,按照1,2步驟進行。4.當子串行只有個...