1. 程式人生 > >列舉排列的兩種方法:遞迴列舉 和 next_permutation()函式

列舉排列的兩種方法:遞迴列舉 和 next_permutation()函式

照著《入門經典》理解整理了一下。

① 以字典序生成1~n的排列 (遞迴列舉)

運用一層層的遞迴,形成一個解答樹。

#include<cstdio>
using namespace std;
void print_permutation_1(int n,int *ar,int cur)
{                   //ar儲存排列,cur表示當前ar中排列到第幾個
	if(cur==n)      //當cur==n,即已排列到末尾,則列印該排列
	{
		for(int i=0; i<n; i++)
			printf("%d ",ar[i]);
		printf("\n"
); } else { for(int i=1; i<=n; i++) //從1~n選擇元素放入當前的ar[cur] { bool ok=true; for(int j=0; j<cur; j++ ) //確保選擇的i還不在排列中 { if(ar[j]==i) ok=false; } if(ok) { ar[cur]=i; print_permutation_1(n,ar,cur+1); //遞迴,cur+1 } } } }

② 以字典序生成可重集的排列 (遞迴列舉)
給定一個數組P,按字典序輸出所有排列,陣列P中會有重複元素。

要先讓陣列P從小到大排好序,答案不能有重複的排列,也不能缺少元素。

#include<cstdio>
using namespace std;
void print_permutation_2(int n,int *P, int *ar,int cur)
{
	if(cur==n)
	{
		for(int i=0; i<n; i++)
			printf("%d ",ar[i]);
		printf("\n");
	}
	else
	{
		for(int i=0; i<n; i++)      //從P[0]到P[n-1]選擇元素放入當前ar[cur]
		{
			//因為之後要通過比較P和ar中該元素的數量來判斷是否選擇該元素,
//所以用以下的if來略過相同元素,以此防止出現相同排列。 if(i==0||(i!=0&&P[i]!=P[i-1])) { int c1=0,c2=0; //c1記錄ar中該元素的數量 for(int j=0; j<cur; j++ ) //c2記錄P中該元素的數量 { if(ar[j]==P[i]) c1++; } for(int j=0; j<n; j++) { if(P[j]==P[i]) c2++; } if(c1<c2) { ar[cur]=P[i]; print_permutation_2(n,P,ar,cur+1); //遞迴,cur+1 } } } } }

③使用C++ STL中的全排列函式next_permutation

對於next_permutation函式,其函式原型為:

 #include <algorithm>

 bool next_permutation(iterator start,iterator end)

若當前序列不存在下一個排列時,函式返回false,否則返回true

給next_permutation()函式提供一個原排列,就可以得到該排列的下一個排列(字典序)並返回true,到了該排列字典序的最後一個排列則返回false。

next_permutation()適用於可重集

#include<cstdio>
#include<algorithm> 
using namespace std;
int main()
{
	int n,P[10];
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",P+i);
	sort(P,P+n);        //生成最小的排列
	do{
		for(int i=0;i<n;i++)
			printf("%d ",P[i]);
		printf("\n");
	}while(next_permutation(P,P+n));  //從 首地址 到 末地址+1
	return 0;
}