求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以后再处吧,有什么问题也可以在下面留言,一起探讨下,谢谢啦~