全排列(二)字典序方式与next_permutation的联系

来源:互联网 发布:路由器限速软件 编辑:程序博客网 时间:2024/06/06 03:31

1、字典序原理


对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。

(1)生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

(2)序数公式
有从 1 到 n 的连续的 n 个自然数,其全排列按照从小到大排列,次序从 0 到 n!-1 ,总共 n! 个。现有其全排列中的一组 “
 
  
  
……
  
”,其全排列次序为:

(3)具体步骤

所谓字典序法就是按照字典排序的思想逐一产生所有排列。比如1,2,3,4四个数字进行全排列,先1234,1243,1324,1342,1423,1432,…4321。

由1243生成1324的过程:



1). 1243从右向左找第一个正序对24

2). 从右向左找第一个大于2的数,3

3). 交换2与3的位置的:1342

4). 把3后面的数字全部反序的:1324               


总结:

1). 从右向左找到第一个正序Pi < Pi+1 (i+1为下标,也即只比较相邻位置)

2). 从右向左找到第一个大于Pi的数Pj  

3). 交换Pi与Pj

4). 将Pj后面的数全部反序,得到一个排序 


这4个步骤保证相邻两个序列的序数(通过序公式计算得)相差为1,准确说是每次增加1,从这个角度来看,每个序列也可以对应一个数值



2、next_permutation方式与字典序方式之间的联系


STL中algorithm算法里面的next_permutation方法便是通过字典序方式实现的,见下面STL源码:


bool next_permutation(_BidirectionalIter __first, _BidirectionalIter __last) {  __STL_REQUIRES(_BidirectionalIter, _BidirectionalIterator);  __STL_REQUIRES(typename iterator_traits<_BidirectionalIter>::value_type,                 _LessThanComparable);  if (__first == __last)    return false;  _BidirectionalIter __i = __first;  ++__i;  if (__i == __last)    return false;  __i = __last;  --__i;  for(;;) {    _BidirectionalIter __ii = __i;    --__i;    if (*__i < *__ii) {       //从右向左找到第一个正序 Pi < Pi+1      _BidirectionalIter __j = __last;        {}      while (!(*__i < *--__j))  //从右向左找到第一个大于Pi的数Pj      iter_swap(__i, __j);    //交换Pi和Pj      reverse(__ii, __last);  //将原Pi位置后面的序列全部反序      return true;    }    if (__i == __first) {      reverse(__first, __last);      return false;    }  }}

自己通过字典序方式实现的next_permutation:

#include<iostream>using namespace std;void swap(int &a,int &b){    int temp;    temp=a;    a=b;    b=temp;}void reverse(int *num,int start,int end){    int i=start;    int j=end;    while(i<j)    {        swap(num[i],num[j]);        i++;        j--;    }}bool next_permutation(int *num,int len){    if(len<2)    {        return false;    }    int i,j;    i=j=len-1;    i--;    for(;i>-1;)    {        if(num[i]<num[j])        {            int k;            for(k=len-1;k>i;k--)            {                if(num[k]>num[i])                {break;}            }            swap(num[i],num[k]);            reverse(num,j,len-1);            return true;        }else        {            i--;            j--;        }    }}int main(){    int num[3]={1,2,3};    cout<<"初始序列: "<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;    do    {          cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;    }while(next_permutation(num,3));      return 0;}




原创粉丝点击