1. 程式人生 > 其它 >遞迴實現全排列&使用STL

遞迴實現全排列&使用STL

技術標籤:遞迴

1.遞迴版

栗子:對1、2、3三個數字進行全排列,並按照字典序從小到大的順序進行輸出1~n的全排列。

【思路】
遞迴角度,如果把問題描述成“輸出1~n這n整數的全排列”——將原問題分為若干子問題:“輸出以1開頭的全排列”、“輸出以2開頭的全排列”、“輸出以3開頭的全排列”。。。“輸出以n開頭的全排列”。

【做法】
陣列P——存放當前的排列;
陣列hashtable[x]——為true時即整數x已經在陣列P中。

(1)按順序將P陣列的第1到n位中填入數字,假設填好了P[1]~P[index-1],
(2)現在正準備P[index]——列舉1~n,如果當前列舉的數字x在前面的P[1] ~ P[n-1]中(即hashtable[x]=0),則將x填入P[index]——同時將hashtable[x]=1;

(3)遞迴:處理P的第index+1位;
(4)遞迴完成時(即已經處理完P[index]為x的子問題,需要還原狀態)——將hashtable[x]設為0(即整數x不在陣列P中),以便讓P[index]填下一個數字

遞迴邊界
當index達到n+1時,說明P的第1~n位都已填好了,即可以把陣列P輸出——表示生成了一個排列,然後直接return。

#include<stdio.h>
#include<stdlib.h>
const int maxn=11;
//P為當前排列,hashtable記錄整數x是否已經在P中
int n,P[maxn],hashtable[maxn]
={false}; //當前處理排列的第index號位 void generateP(int index){ if(index==n+1){//遞迴邊界,已經處理完排列的1~n位 for(int i=1;i<=n;i++){ printf("%d",P[i]);//輸出當前排列 } printf("\n"); return; } for(int x=1;x<=n;x++){//列舉1~n,試圖將x填入P[index] if(hashtable[x]==false){//如果x不在P[0]~P[index-1]中 P[index]
=x;//令P的第index為x,即把x加入當前排列 hashtable[x]=true;//記x已在P中 generateP(index+1);//處理排列的第index+1號位 hashtable[x]=false;//已處理完P[index]為x的子問題,還原狀態 } } } int main(){ n=3;//欲輸出1~3的全排列 generateP(1);//從P[1]開始填 //return 0; system("pause"); }

個人感覺初學者最難理解的是遞迴體中的最後一步,即已處理完P[index]為x的子問題,需要還原狀態(相當於一個清空原來的P陣列的過程);
初學者理解遞迴的好方法是在VS上對遞迴邊界和遞迴體分別設定一個斷點除錯康康

2.STL版

利用#include< algorithm >標頭檔案的next_permutation()函式.

#include <iostream>
#include <algorithm>
using namespace std;
int sum=0;
int main()
{
	int a[6]={1,2,3}; 
	do{
		for(int i=0;i<3;i++)
			cout<<a[i];
		cout<<endl;
		sum++;
	}while(next_permutation(a,a+3));
	cout<<sum;
	system("pause");
}

(1)使用迴圈是因為next_permutation在已經到達全排列的最後一個時會返回false,這樣會方便退出迴圈;
(2)使用do…while語句而不使用while語句是因為序列1 2 3本身也需要輸出,而如果用while語句會直接跳到下一個序列(1 3 2)再輸出(即少了一個1 2 3)。
在這裡插入圖片描述

3.其他方法

其他方法可以參考大佬部落格的兩種方法(用了交換思想)。