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 }

 

 

你可能感兴趣的:(ext)