全排列问题的STL用法(next_permutation类)

标准库全排列next_permutation()

 

在标准库算法中,next_permutation应用在数列操作上比较广泛.这个函数可以计算一组数据的全排列.但是怎么用,原理如何,我做了简单的剖析.

首先查看stl中相关信息.
函数原型:

template<class BidirectionalIterator>
    bool next_permutation(
       BidirectionalIterator _First,
       BidirectionalIterator _Last
    );
template<class BidirectionalIterator, class BinaryPredicate>
    bool next_permutation(
       BidirectionalIterator _First,
       BidirectionalIterator _Last,
       BinaryPredicate _Comp
    );


两个重载函数,第二个带谓词参数_Comp,其中只带两个参数的版本,默认谓词函数为"小于".

返回值:bool类型

分析next_permutation函数执行过程:

假设数列 d1,d2,d3,d4……

范围由[first,last)标记,调用next_permutation使数列逐次增大,这个递增过程按照字典序。例如,在字母表中,abcd的下一单词排列为abdc,但是,有一关键点,如何确定这个下一排列为字典序中的next,而不是next->next->next……

若当前调用排列到达最大字典序,比如dcba,就返回false,同时重新设置该排列为最小字典序。

返回为true表示生成下一排列成功。下面着重分析此过程:

根据标记从后往前比较相邻两数据,若前者小于(默认为小于)后者,标志前者为X1(位置PX)表示将被替换,再次重后往前搜索第一个不小于X1的数据,标记为X2。交换X1,X2,然后把[PX+1,last)标记范围置逆。完成。

要点:为什么这样就可以保证得到的为最小递增。

从位置first开始原数列与新数列不同的数据位置是PX,并且新数据为X2。[PX+1,last)总是递减的,[first,PX)没有改变,因为X2>X1,所以不管X2后面怎样排列都比原数列大,反转[PX+1,last)使此子数列(递增)为最小。从而保证的新数列为原数列的字典序排列next。

明白了这个原理后,看下面例子:

int main(){
int a[] = {3,1,2};
do{
      cout << a[0] << " " << a[1] << " " << a[2] << endl;
}
while (next_permutation(a,a+3));
return 0;
}

输出:312/321          因为原数列不是从最小字典排列开始。

所以要想得到所有全排列

int a[] = {3,1,2};    change to   int a[] = {1,2,3};





另外,库中另一函数prev_permutation与next_permutation相反,由原排列得到字典序中上一次最近排列。

所以

int main(){
int a[] = {3,2,1};
do{
      cout << a[0] << " " << a[1] << " " << a[2] << endl;
}
while (prev_permutation(a,a+3));
return 0;
}

才能得到123的所有排列。

 

---------------------------------------------------------------------------------------------------------------------------------------------

 

上周见到了一道题,实现可输入任意字符串,可给出其所有可能排列组合的情况。想了半天,用自己所了解的知识都是处理不了(当然长久不用,很生疏了,再加之水平本就不高),在网上搜搜,得出了结果,贴出解决方法来,不太跟得上时代发展的同志们可以借鉴一下。 其实也并没有多难,现在C++语言中提供了现成的算法来解决排列组合问题,它们分别是next_permutation 和prev_permutation ,需要注意的是 "如果要走遍所有的排列,你必须先将元素排序"。 以下为转载: <<C++标准程序库>>这本书,在看到"变序性算法"部分的时候,发现两个函数next_permutation, prev_permutation对于我们平时处理排列组合的问题很有帮助,根据书上的介绍写了两个个测试函数:
void func1()
{
    vector<int> v;

    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS( v, "myself: ");
 
    while( next_permutation( v.begin(), v.end() ) )
    {
 
        PRINT_ELEMENTS( v, "");
    }
}


void func2()
{
    vector<int> v;

    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS( v, "myself: ");

    sort(v.begin(), v.end(), greater<int>() ); //增加排序(降序)
 
    while( prev_permutation( v.begin(), v.end() ) )
    {
 
        PRINT_ELEMENTS( v, "");
    }
}

如果以后再遇到类似问题,我们就如此如此,不用再费脑筋,人家有现成的函数,直接拿来用就是了。

 

另外普及一下:

下面这段才是真真的算法,STL里面的源码
  template inline
   bool next_permutation(_BidIt _First, _BidIt _Last)
   { // permute and test for pure ascending, using operator<
   _BidIt _Next = _Last;
   if (_First == _Last || _First == --_Next)
   return (false);
  
   for (; ; )
   { // find rightmost element smaller than successor
   _BidIt _Next1 = _Next;
   if (*--_Next < *_Next1)
   { // swap with rightmost element that's smaller, flip suffix
   _BidIt _Mid = _Last;
   for (; !(*_Next < *--_Mid); )
   ;
   std::iter_swap(_Next, _Mid);
   std::reverse(_Next1, _Last);
   return (true);
   }
  
   if (_Next == _First)
   { // pure descending, flip all
   std::reverse(_First, _Last);
   return (false);
   }
   }
   }
  
  
  Ps: "STL":Standard Template Library,标准模板库(摘录bbs.csdn.net)
   这是最早由Alexander Stepanov和Meng Lee(好像是华人的名字哦)完成,于1994年提交给ANSI/ISO 标准C++委员会并通过而成为标准C++的一部分。望文生义即可知这是一个代码库标准,不是语法标准。简单地说,STL是以C++中的模板语法为基础建立起来的一套包含基础数据结构和算法的代码库。STL的特点是实现了“类型参数化”,即STL的代码中可处理任意自定义类型的对象,如果不使用模板技术的话,这是一件相当困难的事。也因为这个原因,在最新的java及C#语法中均加入了对模板语法的支持,可见其重要性。另外一个有关STL重要的话题是GP(Generic Programming),泛型。这是与面向对象相并列的另外的一个编程模型,它以模板为基础,弱化了实体类型的差异,简化了编程时问题抽象的模型,提供了更好的封装性和弹性,对于繁杂的面向对象编程毫无疑问是一种解脱,至少是精神上的。GP是最近几年软件架构的一个研究热点,但国内真正的应用似乎并不多见。

posted on 2012-08-13 10:17  mycapple  阅读(3189)  评论(1编辑  收藏  举报

导航