STL源码学习----next_permutation和prev_permutation算法

喜夏-厌秋 提交于 2020-02-28 17:23:44

  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 }

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!