1. 程式人生 > >167 請程式設計實現全排列演算法

167 請程式設計實現全排列演算法

67、請程式設計實現全排列演算法。
全排列演算法有兩個比較常見的實現:遞迴排列和字典序排列。

/*
67、請程式設計實現全排列演算法。
全排列演算法有兩個比較常見的實現:遞迴排列和字典序排列。

(1)遞迴實現
從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,
如此遞迴處理,從而得到所有元素的全排列.

(2)字典序排列
思想是讓排列成為可遞推的數列,也就是說從前一狀態的排列,可以推出一種新的狀態,直到最終狀態。
比如說,最初狀態是12345,最終狀態是54321。首先是12345,然後12354,然後12435,12453....逐漸地從後往前遞增。

演算法:
    首先,將待排序列變成有序(升序)序列。然後,從後向前尋找,找到相鄰的兩個元素,Ti<Tj(j=i+1)。
如果沒有找到,則說明整個序列已經是降序排列了,也就是說到達最終狀態54321了。此時,全排列結束。

    接著,如果沒有結束,從後向前找到第一個元素Tk,使得Tk>Ti(很多時候k=j),找到它,交換Ti跟Tk,
並且將Tj到Tn(Tn是最後一個元素)的子序列進行倒置操作。
輸出此序列。並回到第二步繼續尋找ij.

例如839647521是數字1~9的一個排列。從它生成下一個排列的步驟如下:
自右至左找出排列中第一個比右邊數字小的數字4    839647521
在該數字後的數字中找出比4大的數中最小的一個5    839647521
將5與4交換 839657421
將7421倒轉 839651247
所以839647521的下一個排列是839651247。
839651247的下一個排列是839651274。

遞迴實現版本在優化情況下要慢很多,主要原因可能在於太多的函式呼叫開銷,
但在不優化情況下執行比其它二個版本要快,原因可能在於程式結構更簡單,執行的語句較少。
比較而言,遞迴演算法結構簡單,適用於全部計算出所有的排列(因此排列規模不能太大,計算機資源會成為限制);
而字典序排列逐個產生、處理排列,能夠適用於大的排列空間,並且它產生的排列的規律性很強。

*/
#include<iostream> 
#include<stdio.h>
#include<algorithm> 
using namespace std; 
//template <typename T>
int k,n=4;

//遞迴 
void rankRuc(char perm[],int n,int index)  
{ 
	if(index==n)  
    {  
    	printf("%2d:",k++); 
        for(int i=0;i<n;i++)  
            printf("%c",perm[i]);  
        printf("\n");  
        return ;  
    }  	
	for (int i=index;i<n;++i) 
	{ 
		swap(perm[i],perm[index]); 
		rankRuc(perm,n,index+1);   
		swap(perm[i], perm[index]); 
	} 
} 
//字典序 
void rankDic(char perm[],int num) 
{ 
	int i,k;
	
	if (num<1)
		return;
				 
	while(true)
	{ 
		for (i=num-2;i>=0;--i)
			if (perm[i]<perm[i+1]) 
				break; 
		if (i<0) 
			break;  // 已經找到所有排列
			
		for(k=num-1;k>i;--k) 
			if(perm[k]>perm[i]) 
				break; 
		swap(perm[i],perm[k]);
		reverse(perm +i+1,perm+num); 
		
        for(int i=0;i<n;i++)  
            printf("%c",perm[i]);  
        printf("\n"); 
	} 
}

int main() 
{ 
	char perm[]={"abcd"}; 
	k=1;
	rankRuc(perm,4,0); //24個 
	
	printf("字典序排列:\n"); 
	rankDic(perm,4); //24個 
}