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

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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值