天天看點

next_permutation(全排列算法)

      這個序列有六個可能的排列組合:abc,acb,bac,bca,cab,cba。這些排列組合根據less-than操作符做字典順序(lexicographical)的排序。也就是說,abc名列第一,因為每一個元素都小于其後的元素。acb是次一個排列組合,因為它是固定了a(序列内最小元素)之後所做的新組合。

      同樣道理,那些固定b(序列中次小元素)而做的排列組合,在次序上将先于那些固定c而做的排列組合。以bac和bca為例,bac在bca之前,因為次序ac小于序列ca。面對bca,我們可以說其前一個排列組合是bac,而其後一個排列組合是cab。序列abc沒有“前一個”排列組合,cba沒有“後一個”排列組合。

     next_permutation()會取得[first,last)所标示之序列的下一個排列組合,如果沒有下一個排列組合,便傳回false;否則傳回true。這個算法有兩個版本。版本一使用元素型别所提供的less-than操作符來決定下一個排列組合,版本二則是以仿函數comp來決定。

算法思想:

1.首先從最尾端開始往前尋找兩個相鄰元素,令第一進制素為*i,第二進制素為*ii,且滿足*i<*ii。

2.找到這樣一組相鄰元素後,再從最尾端開始往前檢驗,找出第一個大于*i的元素,令為*j,将i,j元素對調(swap)。

3.再将ii之後的所有元素颠倒(reverse)排序。

   舉個執行個體,假設有序列{0,1,2,3,4},下圖便是套用上述演算法則,一步一步獲得“下一個”排列組合。圖中隻框出那符合“一進制素為*i,第二進制素為*ii,且滿足*i<*ii ”的相鄰兩元素,至于尋找适當的j、對調、逆轉等操作并未顯示出。

next_permutation(全排列算法)

以下便是版本一的實作細節。版本二相當類似,就不列出來了。

簡單應用

輸出序列{1,2,3,4}字典序的全排列。

[代碼實作]

拓展

1.能否直接算出集合{1, 2, ..., m}的第n個排列?

舉例說明:如7個數的集合為{1, 2, 3, 4, 5, 6, 7},要求出第n=1654個排列。

(1654 / 6!)取整得2,确定第1位為3(從0開始計數),剩下的6個數{1, 2, 4, 5, 6, 7},求第1654 % 6!=214個序列;

(214 / 5!)取整得1,确定第2位為2,剩下5個數{1, 4, 5, 6, 7},求第214 % 5!=94個序列;

(94 / 4!)取整得3,确定第3位為6,剩下4個數{1, 4, 5, 7},求第94 % 4!=22個序列;

(22 / 3!)取整得3,确定第4位為7,剩下3個數{1, 4, 5},求第22 % 3!=4個序列;

(4 / 2!)得2,确定第5為5,剩下2個數{1, 4};由于4 % 2!=0,故第6位和第7位為增序<1 4>;

是以所有排列為:3267514。

2. 給定一種排列,如何算出這是第幾個排列呢?

和前一個問題的推導過程相反。例如3267514:

後6位的全排列為6!,3為{1, 2, 3 ,4 , 5, 6, 7}中第2個元素(從0開始計數),故2*720=1440;

後5位的全排列為5!,2為{1, 2, 4, 5, 6, 7}中第1個元素,故1*5!=120;

後4位的全排列為4!,6為{1, 4, 5, 6, 7}中第3個元素,故3*4!=72;

後3位的全排列為3!,7為{1, 4, 5, 7}中第3個元素,故3*3!=18;

後2位的全排列為2!,5為{1, 4, 5}中第2個元素,故2*2!=4;

最後2位為增序,是以計數0,求和得:1440+120+72+18+4=1654

這個的代碼實作,可以用一個數組a儲存3267514,然後while調用next_permutation(),用n計數,每次與數組a比較,相等則輸出n;

繼續閱讀