全排列的生成算法

  

【複製轉載】 java



 
 
//全排列的生成算法
//  全排列的生成算法就是對於給定的字符集,用有效的方法將全部可能的全排列無重複無遺漏地枚舉出來。任何n個字符集的排列均可以與1~n的n個數字的排列一一對應,
//  所以在此就以n個數字的排列爲例說明排列的生成法。
//  n個字符的全體排列之間存在一個肯定的線性順序關係。全部的排列中除最後一個排列外,都有一個後繼;除第一個排列外,都有一個前驅。每一個排列的後繼均可以從
//  它的前驅通過最少的變化而獲得,全排列的生成算法就是從第一個排列開始逐個生成全部的排列的方法。
//  全排列的生成法一般有如下幾種:
//  字典序法
//  遞增進位數製法
//  遞減進位數製法
//  鄰位交換法
//  遞歸類算法
//1.字典序法 效率高且順序天然
//  字典序法中,對於數字一、二、3......n的排列,不一樣排列的前後關係是從左到右逐個比較對應的數字的前後來決定的。例如對於5個數字的排列12354和12345,排列12345在前,
//  排列12354在後。按照這樣的規定,5個數字的全部的排列中最前面的是12345,最後面的是54321。
//  字典序算法以下:
//  設P是1~n的一個全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn
//  1)從排列的右端開始,找出第一個比右邊數字小的數字的序號j(j從左端開始計算),即 j=max{i|pi<pi+1}
//  2)在pj的右邊的數字中,找出全部比pj大的數中最小的數字pk,即 k=max{i|pi>pj}(右邊的數從右至左是遞增的,所以k是全部大於pj的數字中序號最大者)
//  3)對換pi,pk
//  4)再將pj+1......pk-1pkpk+1pn倒轉獲得排列p’’=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,這就是排列p的下一個下一個排列。
//  例如839647521是數字1~9的一個排列。從它生成下一個排列的步驟以下:
//  自右至左找出排列中第一個比右邊數字小的數字4 839647521
//  在該數字後的數字中找出比4大的數中最小的一個5 839647521
//  將5與4交換 839657421
//  將7421倒轉 839651247
//  因此839647521的下一個排列是839651247。
//2.遞增進位數製法
//  在遞增進位制數法中,從一個排列求另外一個排列須要用到中介數。若是用 ki表示排列p1p2...pi...pn中元素pi的右邊比pi小的數的個數,則排列的中介數就是對應的
//  排列k1 ...... ki...... kn-1。
//  例如排列839647521的中介數是72642321,七、二、六、......分別是排列中數字八、三、九、......的右邊比它小的數字個數。
//  中介數是計算排列的中間環節。已知一個排列,要求下一個排列,首先肯定其中介數,一個排列的後繼,其中介數是原排列中介數加1,須要注意的是,若是中介數的
//  末位kn-1+1=2,則要向前進位,通常情形,若是ki+1=n-i+1,則要進位,這就是所謂的遞增進位制。例如排列839647521的中介數是72642321,則下一個排列的中介數
//  是67342221+1=67342300(由於1+1=2,因此向前進位,2+1=3,又發生進位,因此下一個中介數是67342300)。
//  獲得中介數後,可根據它還原對應得排列。
//  算法以下:
//  中介數k一、k二、......、kn-1的各位數字順序表示排列中的數字n、n-一、......、2在排列中距右端的的空位數,所以,要按k一、k二、......、kn-1的值從右向左
//  肯定n、n-一、......、2的位置,並逐個放置在排列中:i放在右起的ki+1位,若是某位已放有數字,則該位置不算在內,最後一個空位放1。
//  所以從67342300可獲得排列849617523,它就是839647521的後一個排列。由於9最早放置,k1=6,9放在右起第7位,空出6個空位,而後是放8,k2=7,8放在右起第8位,
//  但9佔用一位,故8應放在右起第9位,餘類推。
//3.遞減進位制數法
//  在遞增進位制數法中,中介數的最低位是逢2進1,進位頻繁,這是一個缺點。把遞增進位制數翻轉,就獲得遞減進位制數。
//  839647521的中介數是67342221(k1k2...kn-1),倒轉成爲12224376(kn-1...k2k1),這是遞減進位制數的中介數:ki(i=n-1,n-2,...,2)位逢i向ki-1位進1。給定排列p,
//  p的下一個排列的中介數定義爲p的中介數加1。例如p=839647521,p的中介數爲12224376,p的下一個排列的中介數爲12224376+1=12224377,由此獲得p的下一個
//  排列爲893647521。
//  給定中介數,可用與遞增進位制數法相似的方法還原出排列。但在遞減進位制數中,能夠不先計算中介數就直接從一個排列求出下一個排列。具體算法以下:
//  1)若是p(i)=n且i<>n,則p(i)與p(i-1)交換
//  2)若是p(n)=n,則找出一個連續遞減序列九、八、......、i,將其從排列左端刪除,再以相反順序加在排列右端,而後將i-1與左邊的數字交換
//  例如p=893647521的下一個排列是983647521。求983647521的下一個排列時,由於9在最左邊且第2位爲8,第3位不是7,因此將8和9從小到大排於最右端364752189,
//  再將7與其左方數字對調獲得983647521的下一個排列是367452189。又例如求987635421的下一個排列,只須要將9876從小到大排到最右端並將5與其左方數字3對調,
//  獲得534216789。
//4.鄰位對換法 效率最高但順序不天然
//  鄰位對換法中下一個排列老是上一個排列某相鄰兩位對換獲得的。以4個元素的排列爲例,將最後的元素4逐次與前面的元素交換,能夠生成4個新排列:
//  1 2 3 4, 1 2 4 3, 1 4 2 3, 4 1 2 3
//  而後將最後一個排列的末尾的兩個元素交換,再逐次將排頭的4與其後的元素交換,又生成四個新排列:
//  4 1 3 2, 1 4 3 2, 1 3 4 2, 1 3 2 4
//  再將最後一個排列的開頭的兩個元素交換,將4從後往前移:
//  3 1 2 4, 3 1 4 2, 3 4 1 2, 4 3 1 2
//  如此循環4!次既可求出所有排列。
//5.元素增值法(n進製法)效率低
//  1)從原始排列p=p1p2......pn開始,第n位加n-1,若是該位的值超過n,則將它除以n,用餘數取代該位,並進位(將第n-1位加1)
//  2)再按一樣方法處理n-1位,n-2位,......,直至再也不發生進位爲止,處理完一個排列就產生了一個新的排列
//  3)將其中有相同元素的排列去掉
//  4)當第一個元素的值>n則結束
//  以3個數一、二、3的排列爲例:原始排列是1 2 3,從它開始,第3個元素是3,3+2=5,5 Mod 3=2,第2個元素是2,2+1=3,因此新排列是1 3 2。經過元素增值,順序產生的
//  排列是:1 2 3,1 3 2,2 1 1,2 1 3,2 2 2,2 3 1,2 3 3,3 1 2,3 2 1
//  有下劃線的排列中存在重複元素,丟棄,餘下的就是所有排列。
//6.遞歸類算法
//  全排列的生成方法用遞歸方式描述比較簡潔,實現的方法也有多種。
//  1)回溯法
//  回溯法一般是構造一顆生成樹。以3個元素爲例;樹的節點有個數據,可取值是一、二、3。若是某個爲0,則表示還沒有取值。
//  初始狀態是(0,0,0),第1個元素值能夠分別挑選1,2,3,所以擴展出3個子結點。用相同方法找出這些結點的第2個元素的可能值,如此反覆進行,一旦出現新結點的3個
//  數據全非零,那就找到了一種全排列方案。當嘗試了全部可能方案,即得到了問題的解答。
//  2)遞歸算法
//  若是用P表示n個元素的排列,而Pi表示不包含元素i的排列,(i)Pi表示在排列Pi前加上前綴i的排列,那麼,n個元素的排列可遞歸定義爲:
//  若是n=1,則排列P只有一個元素i
//  若是n>1,則排列P由排列(i)Pi構成(i=一、二、....、n-1)。
//  根據定義,容易看出若是已經生成了k-1個元素的排列,那麼,k個元素的排列能夠在每一個k-1個元素的排列Pi前添加元素i而生成。例如2個元素的排列是1 2和2 1,對與個
//  元素而言,p1是2 3和3 2,在每一個排列前加上1即生成1 2 3和1 3 2兩個新排列,p2和p3則是1 三、3 1和1 二、2 1,按一樣方法可生成新排列2 1 三、2 3 1和3 1 二、3 2 1。
//
//  3)循環移位法
//  若是已經生成了k-1個元素的排列,則在每一個排列後添加元素k使之成爲k個元素的排列,而後將每一個排列循環左移(右移),每移動一次就產生一個新的排列。
//  例如2個元素的排列是1 2和2 1。在1 2 後加上3成爲新排列1 2 3,將它循環左移可再生成新排列2 3 一、3 1 2,一樣2 1 可生成新排列2 1 三、1 3 2和3 2 1。
相關文章
相關標籤/搜索