zoukankan      html  css  js  c++  java
  • 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 }

     

     

  • 相关阅读:
    [译]Vulkan教程(03)开发环境
    [译]Vulkan教程(02)概况
    [译]Vulkan教程(01)入门
    CSharpGL(57)[译]Vulkan清空屏幕
    CSharpGL(56)[译]Vulkan入门
    CSharpGL(55)我是这样理解PBR的
    CSharpGL(54)用基于图像的光照(IBL)来计算PBR的Specular部分
    [译]背景:着色的物理和数学(4)
    [译]背景:着色的物理和数学(3)
    [译]背景:着色的物理和数学(2)
  • 原文地址:https://www.cnblogs.com/cobbliu/p/2525778.html
Copyright © 2011-2022 走看看