STL源码学习----next_permutation和prev_permutation算法
STL中也提供了迭代器范围内的排列算法,next_permutaion和prev_permutation即是。本文先给出常见的一种字符串全排列算法,然后分析STL提供的next_permutation和prev_permutation算法。
1,一种消重的字符串全排列算法
在字符串全排列中,如果该字符串中存在相同的两个元素,这个字符串的全排列的个数不再是n!,所以要考虑相同元素的情况。下面的实现是利用交换的思想,递归地求解字符串的全排列算法:
1 void swap(char* x, char* y)
2 {
3 char tmp;
4 tmp = *x;
5 *x = *y;
6 *y = tmp;
7 }
8
9 void permute(char *str, int i, int n)
10 {
11 int j;
12
13 if (i == n)
14 printf("%s\n", str);
15 else{
16 for (j = i; j <= n; j++){
17 if(str[i] == str[j] && j != i) //为避免生成重复排列,不同位置的字符相同时不交换
18 continue;
19 swap((str+i), (str+j));
20 permute(str, i+1, n);
21 swap((str+i), (str+j));
22 }
23 }
24 }
上面的代码可以通过permute(str, 0, len)调用来计算str中0~len的全排列。
2,next_permutation和prev_permutation算法
要理解next_permutation和prev_permutation,先看看什么是全排列中的“下一个全排列”,什么是“上一个全排列”。考虑由三个字符组成的序列{a,b,c},这个序列的全排列有6组元素,分别是abc, acb, bac, bca, cab, cba。上面的6组元素是按照字典序排序的。acb即是abc的下一个全排列,同样,cab是cba的上一个全排列。
2.1 next_permutation的实现
next_permutation的实现过程如下:
首先,从最尾端开始往前寻找两个相邻的元素,令第一个元素是i, 第二个元素是ii,且满足i<ii;
然后,再从最尾端开始往前搜索,找出第一个大于i的元素,设其为j;
然后,将i和j对调,再将ii及其后面的所有元素反转。
这样得到的新序列就是“下一个排列”。
下面是next_permutation的详细实现:
1 template <class _BidirectionalIter>
2 bool next_permutation(_BidirectionalIter __first, _BidirectionalIter __last) {
3 __STL_REQUIRES(_BidirectionalIter, _BidirectionalIterator);
4 __STL_REQUIRES(typename iterator_traits<_BidirectionalIter>::value_type,
5 _LessThanComparable);
6 if (__first == __last) //若区间为空,返回false
7 return false;
8 _BidirectionalIter __i = __first;
9 ++__i;
10 if (__i == __last) //若区间中只有一个元素,返回false
11 return false;
12 __i = __last;
13 --__i;
14
15 for(;;) {
16 _BidirectionalIter __ii = __i;
17 --__i;
18 if (*__i < *__ii) { //找到了这样一组i,ii
19 _BidirectionalIter __j = __last;
20 while (!(*__i < *--__j)) //找j
21 {}
22 iter_swap(__i, __j); //交换i,j
23 reverse(__ii, __last); //将[ii,last)内的元素全部逆转
24 return true;
25 }
26 if (__i == __first) { //已经到最前面了,,也就是说当前的序列已经是全排列的最后一个排列了
27 reverse(__first, __last); //将整个序列颠倒
28 return false;
29 }
30 }
31 }
2.2 prev_permutation的实现
prev_permutation的实现过程如下:
首先,从最尾端开始向前寻找两个相邻的元素,令第一个元素为i,第二个元素为ii,且满足i>ii
然后,从最尾端开始往前寻找第一个小于i的元素,令它为j
然后,将i和j对调,再将ii及其之后的所有元素反转
这样得到的序列就是该排列的上一个排列。
1 template <class _BidirectionalIter>
2 bool prev_permutation(_BidirectionalIter __first, _BidirectionalIter __last) {
3 __STL_REQUIRES(_BidirectionalIter, _BidirectionalIterator);
4 __STL_REQUIRES(typename iterator_traits<_BidirectionalIter>::value_type,
5 _LessThanComparable);
6 if (__first == __last)
7 return false;
8 _BidirectionalIter __i = __first;
9 ++__i;
10 if (__i == __last)
11 return false;
12 __i = __last;
13 --__i;
14
15 for(;;) {
16 _BidirectionalIter __ii = __i;
17 --__i;
18 if (*__ii < *__i) { //找到了第一对ii<i的元素
19 _BidirectionalIter __j = __last;
20 while (!(*--__j < *__i)) //找j
21 {}
22 iter_swap(__i, __j); //交换i和j
23 reverse(__ii, __last); //逆转[ii,last)内的所有元素
24 return true;
25 }
26 if (__i == __first) { //搜索到了序列头仍然找不到这样的i,说明当前序列是全排列中的第一个排列
27 reverse(__first, __last); //将当前序列逆转,并返回false
28 return false;
29 }
30 }
31 }