**:
所謂全排列,就是將集合中元素的所有排列情況依次輸出。比如的全排列為:123、132、213、231、312、321,共6種,滿足計算公式n!(n為集合中元素個數,不重複)。
當元素不重複時,全排列採用遞迴思想較容易實現,它的遞迴公式推導步驟類似:
1、要求得123的全排列,只需求得:1並上23的全排列(1 23, 1 32),2並上13的全排列(2 13, 2 31),3並上12的全排列(3 12 321)。
2、對於23的全排列,只需求得2並上3的全排列,3並上2的全排列。步驟1中13、12的全排列也類似。
3、對於3的全排列或者2的全排列,就是它們的本身。遞迴結束。
遞迴實現不重複元素全排列演算法的實現**(c++)如下:
1234567
891011
1213
1415
1617
1819
2021
2223
2425
2627
2829
30
//交換a和b
void swap(
int*a, int
*b)//全排列函式。list:待排元素列表,start:起始位置下標,end:最後乙個有效元素的下乙個下標。
void permutation(
int start, int end, int list)
printf
("\n");
return;}
//對於給定的list[start...end],要使區間中每乙個元素都有放在第一位的機會,
//然後開始遞迴呼叫自身,得到list[start+1...end]的全排列。
for(i = start; i < end; i++
)}
上述程式的呼叫方法為:
1234567
89
#include
using
namespace std;
int main();
permutation(
0, 2, a)
;return0;
}
完整程式見附件:permutation.cpp。
當待排元素列表含有重複項時,上述演算法就需要改進,其中一種方法可以是維護乙個存放不重複排列的集合,每次新生成1個排列,如果集合中不存在這個排列,則插入排列,否則,放棄。
要實現含重複元素的全排列演算法,可以參考stl中next_premutation()函式的實現方法(在algorithm.h中宣告)。該函式會將列表中元素按字典序(wiki)給出全排列中的下乙個排列,它的實現演算法為:
令當前排列為p(0)p(1)p(2)...p(n-1)p(n)。則求它下乙個排列的過程為,
1、從後往前遍歷,找到第乙個p(i)>p(i-1)的元素,記錄下標i。比如排列1、5、2、4、3中滿足條件的元素為4,記下它的下標i = 3,因為p(i)是4,p(i-1)是2,滿足p(i)>p(i-1)。如果找不到這樣的i,則表示該序列已經是字典序中的最後乙個序列,結束演算法。
2、從後往前遍歷,找到第乙個p(j)>p(i-1)的數,記錄下標k。還是上面這個例子,p(i-1)為2,從後往前第乙個大於p(i-1)是p(4)=3,因此記錄下j=4。
3、互換p(i-1)和p(j),得到新序列1、5、3、4、2。
4、將p[i...n]間的元素逆置,返回序列。上述例子中為逆置4和2,得到最終的序列1、5、3、2、4。
用比較通俗的例子解釋一下上述步驟:
假設現在有乙個序列4、6、5、3、2、1,要求得字典序的下乙個序列。首先,從後往前找到第乙個i,使得p(i)>p(i-1),明顯這裡i是1,p(i)=6,這個意思是,在6之後的元素,都是按值遞減的,否則第一步求i的時候也不會找到第2個元素6才滿足條件。現在知道,從i開始到最後,其實是字典序裡的最大序列了(一直按值遞減)。第二步,拿出i的前乙個元素p(i-1)=4,將它與原序列從後往前第乙個大於它的元素交換位置,這裡這個與4交換的元素是5,這樣序列就變成了5、6、4、3、2、1,至此,最高位公升了一級(4->5),接著要把低位的從最大變成最小(就像199之後是200,最高為從1變成2後,要把低位從最大99變成最小00),這裡的低位是最大序列6、4、3、2、1,變成最小序列只需逆置即可,變成1、2、3、4、6,原序列變為5、1、2、3、4、6,即為所求。
實現**如下:
1234567
891011
1213
1415
1617
1819
2021
2223
2425
2627
2829
3031
3233
3435
3637
3839
4041
4243
4445
4647
/**
*如果存在當前序列在字典序中的下乙個排列,則返回true,
*否則返回false。
*/bool next_premutation(
int list[
], int length)}if
(i <=0)
//步驟2:得到j。
for(j = length -
1; j >
0; j--)}
//步驟3:互換list[i-1]和list[j]。
int temp = list[i-1]
; list[i-1]
= list[j]
; list[j]
= temp;
//步驟4:逆置list[i...n]。
int start, end;
for(start = i, end = length-
1; start < end; start++, end--
)return
true
;}
採用這種方法要獲得乙個集合的全排列,可按下面方法呼叫(和stl函式next_permutation()的呼叫方法基本一致):
1234567
891011
1213
1415
16
#include
using
namespace std;
int main();
doprintf
("\n");
}while
(next_premutation(list, 5))
;return0;
}
完整程式見附件:stl_premutation.cpp。
1234567
8910
me@ubuntu:~/premutation$ time ./premutationreal 0m0.227s
user 0m0.212s
sys 0m0.012s
me@ubuntu:~/premutation$ time ./stl_premutation
real 0m0.105s
user 0m0.096s
sys 0m0.004s
由此可見,相比遞迴方法(耗時0m0.227s),第二種方法更加高效(耗時real 0m0.105s),還能防重複。其實想想也能理解,第二種實現方法與stl的next_permutation()基本一致,理論上應該是接近最優解了吧。但是,採用第二種方法計算全排列也有乙個前提,就是這個待排序列必須是字典序中的最小數,因此,需要在迴圈呼叫next_premutation()前將序列排序,否則只能得到當前序列之後的"全"排列。
遞迴演算法 求序列的全排列
書本 windows程式設計 功能 輸出全部的排列情況 檔案 全排列.cpp include using namespace std 交換兩個元素的函式 templateinline void swap type a,type b 取兩個元素的引用,等會來交換 這個是乙個遞迴為了輸出全部的排列情況 ...
求所有子串行 全排列
假設字串為abc,求解其所有的子串行。a b c ab ac bc abc 思路 直接 public static void printallpermutations1 string str public static void process1 char chs,int i for int j i...
求冪,全排列基本演算法
看這些演算法很費,用了兩個多小時 1 全排列 迭代思想,未考慮重複元素 include using namespace std void swap char a,char b 全排列思想 1 2個數全排列 ab ba,即第乙個數與後面的數交換。視a不動,則bc排列 abc acb 2 迭代 把最後兩...