全排列的遞迴和非遞迴的實現

2022-09-17 15:42:13 字數 3481 閱讀 2273

用c++寫乙個函式, 如 foo(const char *str), 列印出 str 的全排列,

如 abc 的全排列: abc, acb, bca, dac, cab, cba

為方便起見,用123來示例下。123的全排列有123、132、213、231、312、321這六種。

首先考慮213和321這二個數是如何得出的。顯然這二個都是123中的1與後面兩數交換得到的。

然後可以將123的第二個數和每三個數交換得到132。同理可以根據213和321來得231和312。因此可以知道——全排列就是從第乙個數字起每個數分別與它後面的數字交換。找到這個規律後,遞迴的**就很容易寫出來了:

[cpp]view plain

copy

//全排列的遞迴實現

#include 

#include 

void

swap(

char

*a, 

char

*b)  

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

void

allrange(

char

*pszstr, 

intk, 

intm)  

else

}  }  void

foo(

char

*pszstr)  

intmain()  

執行結果如下:

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

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

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

們先嘗試加個這樣的判斷——如果乙個數與後面的數字相同那麼這二個數就不交換了。如122,第乙個數與後面交換得212、221。然後122中第二數就不

用與第三個數交換了,但對212,它第二個數與第三個數是不相同的,交換之後得到221。與由122中第乙個數與第三個數交換所得的221重複了。所以這

個方法不行。

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

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

[cpp]view plain

copy

//去重全排列的遞迴實現

#include 

#include 

void

swap(

char

*a, 

char

*b)  

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

bool

isswap(

char

*pszstr, 

intnbegin, 

intnend)  

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

void

allrange(

char

*pszstr, 

intk, 

intm)  

else

}  }  }  

void

foo(

char

*pszstr)  

intmain()  

執行結果如下:

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

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

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

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

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

[cpp]view plain

copy

//全排列的非遞迴實現

#include 

#include 

#include 

void

swap(

char

*a, 

char

*b)  

//反轉區間

void

reverse(

char

*a, 

char

*b)  

//下乙個排列

bool

next_permutation(

char

a)  

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

return

false

;  }  

intqsortcmp(

const

void

*pa, 

const

void

*pb)  

intmain()  

while

(next_permutation(sztextstr));  

return

0;  

}  測試一下,結果如下所示:

將字串改成"cba"會輸出:

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

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

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

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

如果覺得本文對您有幫助,『頂』支援一下,您的支援是我寫作最大的動力,謝謝。

全排列的遞迴和非遞迴實現

1.全排列的遞迴實現 全排列就是從第乙個數字起每個數分別與它後面的數字交換 include include include using namespace std 遞迴實現 void allrange string s,int k,int m else intmain 執行結果 第1個排列是 abc...

全排列的非遞迴實現

以前寫過全排列的遞迴演算法,思路和實現都比較簡單。上週練習python,遇到了全排列的問題,就想著用非遞迴實現實現以下。沒想到從中午想到了半夜,草稿用了好幾張,也沒想到好的idea。第二天早上在地鐵上,在手機撥號頁面演示了一會,竟然有了思路。假定 從小打大排序,稱為正序,反之稱為逆序,我的思路是 以...

全排列的遞迴與非遞迴實現

1 全排列 將n個不同元素按照不同的順序進行排列,一般要求所有的排列方式,或者滿足某些要求的排列方式,比如先後順序的限制 2 遞迴實現全排列 eg 對 a b c d 進行全排列,可以按照以下的步驟 1.a後面加上 b c d 的全排列 2.b後面加上 a c d 的全排列 3.c後面加上 b a ...