1. 程式人生 > 其它 >關於全排列--手寫next_permutation()函式

關於全排列--手寫next_permutation()函式

求1~n的全排列,有兩種方法,dfs和藉助next_permutation()函式,這裡我淺談後者。

next_permutation()原型是bool next_permutation(iterator start,iterator end),在c++庫<algorithm>中,既找陣列的下一個排列,這裡的陣列可以是整型、字元型等。

程式碼實現1~3的全排列:

#include <iostream>  
#include <algorithm>  
using namespace std;  
int main()  
{  
    int num[3
]={1,2,3}; do { cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl; }while(next_permutation(num,num+3)); return 0; }

括號內的範圍類似sort函式,sort(首地址,首地址+個數),考慮升降冪的話加一個cmp函式來判斷。

這樣直接用很簡單,但是我想講一講手寫next_permutaion.

核心就是:如何找下一個排列,規律為何?

清楚一點:每次next_permutation確定的下一個排列實則只交換了兩個數,如原來是1234,下一個為1243,交換了4和3.

如9237740這個排列,下一個是誰?如何做交換?

核心:從後往前找,找一對遞增的相鄰數,如40不是遞增,74也不是,37是,記這對遞增數的前一個數3為關鍵數,從後面找比它大的數中的最小數4,做交換變為7247730,再將4後面做一次顛倒得到7240377,即9237740的下一個全排列數。

換一種理解,我們從後找發現7740是逆序,無法通過交換得到一個比原數大的數,往前走,37740非逆序,將3和4交換,變為47730,做一次翻轉,得到9237740,正好是9237740的下一個排列數。

看程式碼吧:

#include <iostream>  
using namespace std;

const
int N = 20; int a[N]; int n; void swap(int *a,int *b) { int t = *a; *a = *b; *b = t; } void reverse(int *a,int *b) { while (a<b) { swap(a++,b--); } } bool next_per() { int *end = a + 1 + n; // 尾指標 if (end == a + 1) return false; // n = 0 end--; int *p,*pp,*find; p = end;// p從後往前遍歷 while (p != a + 1) { pp = p; // pp為p右相鄰指標 p--; if (*p < *pp) {// 相鄰遞增 find = end; // 從後往前 找最小的大於*p的數 同時也是第一個大於*p 的數 while (*find <= *p) find--; swap(p,find);// 交換 reverse(pp,end); // 翻轉 return true; } } return false; } int main() { cin>>n; for (int i = 1; i <= n;i++) { a[i] = i;// 初始化 } do{ for (int i = 1; i <= n; i++) cout<<a[i]; cout<<"\n"; }while(next_per()); return 0; }

好了,手寫全排列到這了,dfs以後再處吧,有什麼問題也可以在下面留言,一起探討下,謝謝啦~