求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;
}