排列组合(STL算法中next_permutation和prev_permutation剖析)
STL提供了两个用来计算排列组合关系的算法,分别是next_permucation和prev_permutation。解决全排列问题。
首先我们必须了解什么是“下一个”排列组合,什么是“前一个”排列组合。考虑三个字符所组成的序列{a,b, c)。这个序列有六个可能的排列组合: abc,acb,bac,bca,cab,cba。
这些排列组合根据less-than操作符做字典顺序(按照升序排列)的排序。也就是说,abc名列第一,因为每一个元素都小于其后的元素。acb是次一个排列组合,因为它是固定了a(序列内最小元素)之后所做的新组合。同样道理,那些固定b(序列内次小元素)而做的排列组合,在次序上将先于那些固定c而做的排列组合。以bac和 bca为例,bac在 bca之前,因为序列ac小于序列ca。面对bca,我们可以说其前一个排列组合是bac,而其后一个排列组合是cab。序列abc没有“前一个”排列组合,cba没有“后一个”排列组合。
next_permutation
next_permutation ( )会取得〔 first,last)所标示之序列的下一个排列组合。如果没有下一个排列组合,便返回false;否则返回true。
next_permutation 算法过程:

首先,从最尾端开始往前寻找两个相邻元素,令第一元素为*i,第二元素为 *ii,且满足*i< *ii。找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于*i的元素,令为*j,将 i,j元素对调,再将 ii 之后的所有元素颠倒排列。此即所求之“下一个”排列组合。
e.g.以序列{0,1,2,3,4}为例,获得”下一个”排列组合。

输入序列{0,1,2,3,4}
第一元素为*i == 3,第二元素为 *ii == 4,且满足*i< *ii. 找到这样一组相邻元素(3 4)
从最尾端开始往前检验,找出第一个大于*i的元素,令为*j == 4
将 i,j元素对调(*i == 4*j == 3),再将 ii 之后的所有元素颠倒排列
输出“下一个”排列组合{0,1,2,4,3};
e.g.以序列{0,1,2,4,3}为例,获得”下一个”排列组合。

输入序列{0,1,2,4,3}
第一元素为*i == 2,第二元素为 *ii == 4,且满足*i< *ii. 找到这样一组相邻元素(2 4)
从最尾端开始往前检验,找出第一个大于*i的元素,令为*j == 3
将 i,j元素对调(*i == 3 *j == 2),再将 ii 之后的所有元素颠倒排列 (4 2-->2 4)
输出“下一个”排列组合{0,1,3,2,4};


代码测试求全排列问题:
#include<iostream>
using namespace std;
bool test_next_permutation(char* first, char* last)
{
if(first == last) return false;
char* i = first;
++i;
if(i == last) return false;//只有一个元素
i = last; //i指向尾部
--i;
for(;;)
{
char* ii = i;
--i;
//以上,以确定一组相邻元素(升序)
if(*i < *ii){ //如果前一个小
char * j = last; //j指向尾部(尾元素下一个)
while(!(*i < *--j));//由尾向前,找到比*i大的元素
swap(*i,*j); //交换i,j元素
reverse(ii,last);//将ii后全部元素逆序排序
return true;
}
if(i == first){ //进行首元素
reverse(first, last);//全部逆向排序
return false;
}
}
}
int main()
{
char s[5] = "0123";
do
{
cout<<s<<endl;
}while(test_next_permutation(s,s+4));
return 0;
}
prev_permutation

