總結 三種全排列演算法

2021-04-14 16:01:30 字數 3108 閱讀 9787

1、遞迴演算法

演算法書上有詳細的解釋,複製如下:

設r=是要進行排列的n個元素,ri=r-.集x中元素的全排列記為perm(x),(ri)perm(x)表示在全排列perm(x)的每乙個排列前加上字首ri得到的排列.r的全排列可歸納定義如下:

當n=1時,perm(r)=,r是集合r中唯一的元素.

當n>1時,perm(r)由(r1)perm(r1),(r2)perm(r2),....(rn)perm(rn)構成

**如下:

2、stl模板中

next_permutation

對范型演算法next_permutation的原始碼分析

// template function next_permutation

templateinline

bool next_permutation(_bi _f, _bi _l)

if (_i == _f)

next_permutation()將[first,last)標記的排列重排為下乙個排列,如果不存在下乙個排列,則返回false。

原始碼分析(假設有容器):

(1)、由於范型演算法一般是應用在容器上,而容器是用其首元素和最後元素的下乙個位置來表示其範圍的,所以next_permutation()處理的 [first,last)為半開半閉區間,對於演算法內部,則很明顯要先對_i進行遞減操作,使其指向容器的最後乙個元素,如果和首元素位址相同,則為單元素容器,不存在下乙個排列。

(2)、把_i的iter賦給_lp,注意對於第一次迴圈_i是指向最後元素了(因為(1)中有操作--i)。_ip和_i指向容器元素5時,(3)的條件滿足。

(3)、因其處在無限迴圈for(;;)中,故在容器中從後往前找第乙個非逆序(後面的元素不小於其緊挨的之前的元素*--i<*_lp)(3,5),此時_i指向3,_lp指向5。

(4)、對_i到最後乙個元素之間的元素查詢是否有大於_i所指向的元素(3),有!此時_j指向元素5大於3。如果沒有,那麼該for迴圈最後_i,_j指向同一元素3,同樣結束該for迴圈。

(5)、交換_i,_j所指向的元素。得到容器

(6)、對於_lp和_l之間的元素重新按反序排列,原來為逆序532,現在重排為235,於是得到容易  ,即是容器的下乙個排列。

(7)、對於已經排成完全逆序的容器,如,則先將其按反序重排為完全順序,然後返回false,因為其不存在下乙個排列。

注意:在求容器的下乙個排列時,總是要保證容器靠前的元素最小的不同排列,且如果得到完全逆序容器,則返回false,所以如果要求乙個容器的全排列,應該先對該容器進行順序排序,最後一點經過next_permutation()操作最後返回false後的容器為順序的,應為該演算法對完全逆序容器是先反序排列了之後再返回false的。

此演算法進行的全排列的話,需要首先對排列的陣列進行排序。假設其為公升序排列。

首先,從右至左,找出第乙個按公升序排列的數對,比如上列中中的(3,5),這說明從五之後的都是逆序,既降序排列。

然後從降序排列的數中(5,4,2),找到第乙個比3大的數,並與3交換,因此可得到;

交換後可以保證從5開始之後的數都是降序的(5,3,2),對他進行反轉,既(2,3,5),總的為(4,2,3,5),此即為stl演算法中的下乙個排列。

當對(4,2,3,5)之後的排列,其實是會對子串(2,3,5)進行全排列的輸出。因此感覺其是遞迴回溯的思想。

3、奇妙的非遞迴演算法取全排列

摘抄如下:

1. 演算法思路:

試想n個自然數a1,a2,...,an的全排列是n!個,那麼,對於每種排列,我將其列印出來,遍歷一遍至少是o(n!)的複雜度,那麼能不能在就在這個複雜度內用非遞迴解決這個問題呢?我的想法就是從這點開始成形.同時借助了建模的思想分析的.

無論如何,我首先選取乙個自然數 a1 出來,放到存放位址的中間, 那麼在a1的周圍存在兩個位置,乙個是a1的左邊,乙個是a1的右邊,那麼,我抽出另一自然數出來(假設是ak),便有兩種存放的可能性:

(ak,a1) , (a1,ak).

如果我將1周圍的兩個位置用0和1來表示,如果放在左邊,那麼左邊用1表示,反之用0表示.反正只能放乙個地方,那麼這兩個位置合起來不是10,就是01,化成十進位制便是2或者1.

同理,如果第第二個數a2放在a1的左邊,也就是第一輪獲取了2,排列就是(a2,a1),當第三個數(假設是a3)到來時,那麼它就存在三個位置可放了,分別是a2的左邊,a2和a1之間,a1的右邊,同樣用0和1標誌每個位置,如果a3放在a2左邊,那麼構成100(十進位制4),如果中間則構成010(十進位制2),a1的右邊001(十進位制1),那麼可以用第二個值來表徵第三個數擺放的位置.

如此,細心的玩家已經大概想明白了我要幹嘛了,對,如此以往,可以將全部n個自然數用n-1個數值來替代(這些數字是有規律的,2的次方),接下來,我想到的便是,一定要找到這些數字和它在全排列中一一對應的關係,那麼問題便解決了.

我們拿3個自然數的例子來分析,3!=6,對於3個自然數的全排列我將其編號1-6組,每組按上面的規則應該可以唯一對應乙個序列,如將第一組對應於(1,1),  是按上面規則來的.  第二組對應於(1,2), 第三組對應於(2,1), 第四組對應於(2,2), 第五組對應於(4,1), 第六組對應於(4,2). 於是, 我只要分析出乙個計算規則便可以只要遍歷這n!個分組,便可以求出每個分組對應的排列了.

很顯然, 由p(m,m)的計算規則,p(m,m)=m*(m-1)*...*1可以看出,如果我在第2個到來時,將m = p(m,m)%2 同時,p=p/2,那麼,可以很好的處理第二個到來時所做的定址選擇:如果m是0,那麼就放右邊,如果m是1,就放左邊,而對應於第三個到來時,同樣如此,那麼這個存放規則便生成了.於是遍歷n!次,按輾轉法則,便可以直接生成每個組對應的全排列了.

個人感覺作者的思路說的有些太複雜了,這個演算法的目的就是說對於n個數,一共有n!種排列,假設從0-(n!-1)個數字中每乙個數字可以對應一種排列,而如何建立其這n!個數和某種排列的對應關係,是這個演算法的關鍵。

我們總是假設第乙個數已經放好了,對於第二個數,有兩種選擇,對於第三個數有三種選擇。。。。第k個數有k種選擇。對於乙個在0-(n!-1)之間的數字m,和第二個數的選擇,可以用公式m%2,得到的結果為0或者1,既可以代表第二個數的兩種選擇,之後必須用m = m/2,因為第二個數的已經做了選擇,剩餘的m,是之後數字的選擇的結果。對於第k個數,公式為 m%k, 和 m=m/k。

**於分析(補)。。。。

全排列的三種寫法

給定n個不同的數,要求生成這n個數的全排列!解法1 遞迴思想,首先選定第乙個數 有n種不同的選擇方式 然後對剩下的n 1個數進行全排列 根據此步驟,對剩下n 1數的全排列進行遞迴。void permutation vector ivec,int level for int i level i ive...

全排列的三種實現

題目描述 生成由1至n個數的全排列。輸入 輸入包含多組樣例,每組樣例輸入乙個整數n 1 n 9 輸出 按照字典序反序輸出,每個數值之間用空格隔開。每個排列單獨佔一行。逆字典序 include include include int num 10 int visited 10 int n,t void...

c 實現全排列的三種方式

include include include include using namespace std const int maxn 10 bool visit maxn 判斷某個元素是否被訪問過 char sequence maxn 存放找到的全排列 void getpermutation str...