遞迴實現全排列&使用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;
(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.其他方法
其他方法可以參考大佬部落格的兩種方法(用了交換思想)。