前言
众所周知,c++中标准库已经包含了这两个排列的函数。
即,next_permutation()和prev_permutation()
但我觉得研究一下排列对算法来说绝对是有益无害,同时也能让我们在解题中更加顺手。
通晓原理才是永远的神~
PS::不过实际解题中,效率最关键,最好还是直接使用排序函数。
上一个排列prev_permutation()函数
要实现这个函数,我们得了解我们是如何判断上一个排列的。
举个例子,21045的上一个排列是20541.。
发现规律了嘛?当前的这个数5->4->0都是降序,而0->1突然变为升序。也就是说,从末尾开始降序(等于的情况也可),我们要一直找到一个升序点。
这个例子中,找到升序点,也就是1后,我们将0与1交换,然后对{1,4,5}进行从大到小的排序接在0的后面,就得到了原数的上一个排列。
接着我们可以快速的写出上排列的一个函数(假定以字符指针和长度作为函数参数):
void pre_permu(char *a,size_t n)
{
int poi=-1;
char ram;
for (int i = n - 1; i > 0; i--)//找到不符合降序的位置
{
if (a[i] < a[i - 1])
{
poi = i;
break;
}
}
ram = a[poi - 1];//交换
a[poi - 1] = a[poi];
a[poi] = ram;
sort(a + poi-1, a + n,greater<char>());//从大到小排序,greater是c++标准库的一个结构体,定义了比较原则
if(poi==-1) cout<<"error"<<endl;
}
下一个排序next_permutation()函数
懂得了如何判断上一个排序,下一个函数自然也是手到擒来!
同样是先从末尾判断,不过这次是升序。
以12543为例,它的下一个排列是13245.
首先找到冲突点5->2,然后注意不能直接交换,与上一个函数不同的地方就在这里。
需要我们重新从末尾向冲突边界(此处为5)遍历,记录大于冲突值(此处为2)的最小值(也就是3),然后交换。
稍有不同,但是无伤大雅,下面是具体函数。
void next_permu(char *a,size_t n)
{
int poi = -1,poi2=-1;
int mi=1000,ram;
for (int i = n - 1; i > 0; i--)//找冲突值
{
if (a[i] > a[i - 1])
{
poi = i;
break;
}
}
for (int i = n - 1; i > 0; i--)//找到大于冲突值的最小值
{
if (a[i]<mi && a[i] > a[poi - 1])
{
poi2 = i;
}
}
if(poi==-1)cout << "error" << endl;
ram = a[poi];
a[poi] = a[poi2];
a[poi2] = ram;
sort(a + poi - 1, a + n);//默认为从小到大排序
}