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 }
来源:https://www.cnblogs.com/cobbliu/archive/2012/05/30/2525778.html