这两个函数主要实现的功能是排列组合方面的操作,以前对于序列的全排列算法递归的、非递归的都看过几个,最近看《STL源码剖析》,感觉SGI STL里面的实现也颇为巧妙。
首先说下next_permutation,该函数求当前序列的下一个组合,算法很巧妙,具体思想是:从最尾端开始寻找两个相邻的元素,令第一个元素是i,第二个元素是ii,且*i < *ii,找到这样一组元素后,再从尾端开始往前选择第一个大于*i的元素,将i、j元素对调,然后将ii之后的所有元素逆序排列,就得到了下一个组合。源码如下:
算法的原理很简单,但是关键问题是为什么这样可以得到当前序列的下一个组合?
首先从后往前寻找一个数对,且第一个元素小于第二个元素,这个很好理解,因为全排列就是把数列从完全顺序排序一步步转换到完全的逆序,这是目前从后往前第一个顺序的数对,所以肯定在此处下手。关键是为何把i与从后往前第一个大于它的元素交换,并且颠倒[i+1, last)的元素就可以得到下个组合?根据算法思想可以肯定的是[first, i)这部分元素与原来的序列肯定相同,把i与从后往前第一个大于它的元素j替换,则不管后面如何排列,由于*j > *i,得到的新排列肯定大于原来的排列,下面需要说明的就是如何保证这个新排列是原来排列的下一个,而不是下一个的下一个或者…。
i和ii是从后往前第一个顺序的数对,这就可以肯定[ii, last)这部分区域肯定是逆序的,所以*(j-1) > *j并且*j > *(j+1),而*j是从后往前第一个大于*i的元素,故*(j-1)>*j >*i > *(j+1),所以*i与*j置换后[ii, last)这部分数据仍然是逆序的,既然如此,我们把这部分数据reverse一下,就得到了完全顺序的序列,在排列组合中,这是最小的情况,因此可以肯定得到的新的排列是原序列的下一个组合。
理解了next_permutation的原理后,再来看prev_permutation就简单多了。Prev_mutation的思想是:从后往前寻早第一个逆序的数对,第一个元素为*i,第二个元素为*ii,且*i > *ii,再从后往前寻找第一个小于*i的元素*j,将i、j对调,然后将ii之后的元素颠倒就得到了前一个组合。源码如下:
prev_mutation和next_mutation的思想很相似,这里简单说下。[ii, last)直接的元素是顺序排列的,因为i和ii是第一个逆序的数对,*j是从后往前第一个小于*i的元素,可以肯定*j是ii后小于*i的最大的元素,i、j置换,i之前的元素没变,而*j < *i,这样就保证新序列小于原序列,同时由于*(j-1) < *j < *i < *(j+1),故置换后,ii之后的序列仍然是顺序排列的,把这部分数据reverse,就得到了这部分数据组合的最大情况,故是原序列的上一个排列。
[参考侯捷先生著作《STL源码剖析》]