陣列的全排列

2021-07-11 05:07:24 字數 3946 閱讀 8106

學過數學的人都知道,全排列的意思是什麼。現在如何用計算機的程式語言實現陣列的全排列呢?

陣列的全排列可用於求解八皇后問題,具體參見:全排列解決八皇后問題。與此同時,全排列經常會出現在筆試或者面試,如求字串的全排列。之所以那它作為考題,因為它難度適中,既可以考察遞迴實現,又能進一步考察非遞迴的實現,便於區分出考生的水平。所以,掌握它很重要。

全排列表示把集合中元素的所有按照一定的順序排列起來,使用p(n, n) = n!表示n個元素全排列的個數。p(n, n)中的第乙個n表示元素的個數,第二個n表示取多少個元素進行排列。

給定乙個n個元素陣列,其全排列的過程可以描述如下:

(1)任意取乙個元素放在第乙個位置,則有n種選擇;

(2)再剩下的n-1個元素中再取乙個元素放在第二個位置則有n-1種選擇,此時可以看做對n-1個元素進行全排列;

(3)重複第二步,直到對最後乙個元素進行全排列,即最後乙個元素放在最後乙個位置,全排列結束。

以陣列為例,其全排列的過程如下:

(1)1後面跟(2,3)的全排列;

(2)2後面跟(1,3)的全排列;

(3)3後面跟(1,2)的全排列。

由於遞迴將問題逐級分解,因此相對比較容易理解,但是需要消耗大量的棧空間,如果函式棧空間不夠,那麼就執行不下去了,而且函式呼叫開銷也比較大。

#include 

using

namespace

std;

int sum=0; //全排列個數

//列印陣列內容

void print(int

array,int len)\n");

}//實現兩數交換

void swap(int* o,int i,int j)

//遞迴實現陣列全排列並列印

void permutation(int

array,int len,int index)

else

for(int i=index;i//將第i個元素交換至當前index下標處

swap(array,index,i);

//以遞迴的方式對剩下元素進行全排列

permutation(array,len,index+1);

//將第i個元素交換回原處

swap(array,index,i);

}}int main();

permutation(array,3,0);

cout

<<"sum:"

《還是以陣列為例,如果陣列中有重複的元素,變成了,那麼它的全排列就不能完全按照上面的方法求解,需要做稍微的改動。

因為全排列是將不同元素依次換到當前位置後,再對後面的元素求全排列。如果將重複的元素多次換到當前位置的話,那麼就會出現相同的排列。為了避免,我們禁止將相同的元素多次換到當前位置即可。

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

這樣我們也得到了在全排列中去掉重複的規則——去重的全排列就是從第乙個數字起每個數分別與它後面非重複出現的數字交換。

修改後的**如下:

//是否交換

bool isswap(int array,int len,int

index)

//遞迴實現有重複元素的陣列全排列

void permutation(int array,int len,int

index)

else

for(int i=index;iif(isswap(array,len,i))

}}

全排列的非遞迴實現需要用到元素排列後的字典序。所謂的字典序就是按照元素的大小對形成排列進行排序。比如和,因為前乙個排列的第二元素2是小於後乙個排列的第二元素3,所以前乙個排列排在前面,後乙個排列排在後面。

利用字典序來生成全排列的演算法思想是:將集合a中的元素的排列,與某種順序建立一一對映的關係,按照這種順序,將集合的所有排列全部輸出。這種順序需要保證,既可以輸出全部的排列,又不能重複輸出某種排列。字典序就是用此種思想輸出全排列的一種方式。

給定陣列a[n],那麼使用字典序輸出全排列的方法基本過程描述如下:

(1)將a按元素大小遞增排序,形成字典序最小的排列;

(2)左起從a[0]開始尋找最後乙個元素a[k],滿足a[

k]k+1]

(k1)

,n為元素個數;

(3)從a[k+1]向右開始尋找最小的乙個a[i],使得a[i]>a[k];

(4)交換a[k]與a[i];

(5)對於a[k+1,n-1],反轉該區間內元素的順序,即a[k+1]與a[n]交換,a[k+2]與a[n-1]交換,……,這樣就得到了a[1…n]在字典序中的下乙個排列。

(6)重複步驟(2)至(5),直到a按元素大小遞減排序,即第二步找不到滿足條件的a[k]。

總的來說字典序生成全排列的就是:先排序,再由後向前找第乙個替換點,然後由向後向前找第乙個比替換點所在元素大的數與替換點交換,最後顛倒替換點後的所有資料。

這裡之所以都是從後向前尋找,因為可以提交效率。替換點後面的元素一定是遞減排列的,所以只需要從後向前找第乙個大於替換點所在的元素就行了。最後顛倒替換點後的所有資料也是讓替換點後的資料排列成字典序最小的狀態。

以陣列a[3]=為例,字典序輸出全排列的具體實現過程如下:

(1)按字典序遞增將a排好序,a=,這是字典序最小的第乙個排列;

(2)從最後a[2]開始向前尋找第乙個元素a[k],使得a[k]

優點:

(1)使用迭代的方式,避免了遞迴實現的函式棧空間的大量消耗和函式呼叫的時間開銷;

(2)無需考慮陣列中出現的重複元素。

缺點:

(1)對陣列的排序,增加了時間開銷。其實這個可以優化,後面再說;

(2)每次尋找下乙個排列時都要對替換點後的元素進行反轉,這也增加了時間開銷。

#include

using

namespace

std;

int sum=0;

//列印陣列內容

void print(int

array,int len)\n");

}//實現兩數交換

void swap(int* o,int i,int j)

//實現陣列顛倒

void reverse(int

array,int s,int e)

}//快排比較函式

int compare(const

void* a,const

void* b)

//字典序實現陣列全排列並列印

void permutation(int

array,int len)

if(pos==-1)

return;//排列結束

//從後向前尋找第乙個大於替換點所在元素

int subsindex=-1;

for(int i=len-1;i>pos;--i)

if(array[i]>array[pos])

//交換

swap(array,pos,subsindex);

//顛倒

reverse(array,pos+1,len-1);

++sum;

print(array,len);

}}int main();

permutation(a,4);

cout

<<"sum:"

《對於有重複元素的陣列進行全排列,同樣有效。

使用字典序輸出集合的全排列需要注意,因為字典序涉及兩個排列之間的比較,對於元素集合不方便比較的情況,可以將它們在陣列中的索引作為元素,按照字典序生成索引的全排列,然後按照索引輸出對應集合元素的排列。這也就省去了

對陣列進行排序的操作。

[1][2]

[3]

陣列的全排列

1.問題背景 學過數學的人都知道,全排列的意思是什麼。現在如何用計算機的程式語言實現陣列的全排列呢?陣列的全排列可用於求解八皇后問題。與此同時,全排列經常會出現在筆試或者面試,如求字串的全排列。全排列表示把集合中元素的所有按照一定的順序排列起來,使用p n,n n 表示n個元素全排列的個數。p n,...

陣列的全排列演算法

求給定陣列的全排列。如 輸入 1,2,3 輸出 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 3 1 2 思路 這裡借用別人的解題思路,我就不重述了 1 首先看最後兩個數4,5。它們的全排列為4 5和5 4,即以4開頭的5的全排列和以5開頭的4的全排列。由於乙個數的全排列就是其本身,從...

輸出陣列全排列

include include using namespace std 輸出陣列全排列 演算法思路 1 n個元素的全排列 n 1個元素的全排列 另乙個元素作為字首 2 出口 如果只有乙個元素的全排列,則說明已經排完,則輸出陣列 3 不斷將每個元素放作第乙個元素,然後將這個元素作為字首,並將其餘元素繼...