全排列的两种常见方法(递归,STL)

零、全排列

       从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。


一、递归方法(非字典序)(dfs)

       当我们要统计全排列的情况时,最直接的就是枚举,数一数,为了高效的计数,比如123的全排列,我们通常先固定第一个位置上的数“1”,然后后面可以是2,3或者3,2;然后再固定2,数1,3、3,1;最后3,1,2和3,2,1。

            那么怎么用递归来实现呢?我们发现在所有的枚举中,“123”始终是参考源,我们不能把它弄丢了,所以在交换后面的数时,最后要原封不动的换回来,这里就在一个递归中用两个swap。然后要有一个移动的头指针,在递归中开一个循环,头指针后移后调用函数本身递归,之前之后swap,这样就能达到全排列的目的。结束递归的条件就是头指针指到末尾了,就在这时候打印数组。全排列的递归方法本质就是一种“循环”。

代码示例:

//递归全排列
#include<bits/stdc++.h>
using namespace std;

int n;
int arr[50];
long long num;

void full_permutation(int first)
{
    if(first==n){
        num++;
        //for(int i=1;i<n;++i) cout<<arr[i]<<' ';
        //cout<<arr[n]<<endl;
        return ;
    }
    for(int i=first;i<=n;++i){
        swap(arr[first],arr[i]);
        full_permutation(first+1);
        swap(arr[first],arr[i]);
    }
}

int main()
{
    ios::sync_with_stdio(false);
    //1~n
    cin>>n;
    num=0;
    for(int i=1;i<=n;++i) arr[i]=i;
    full_permutation(1);
    cout<<num<<"个"<<endl;
    return 0;
}



二、STL方法(字典序)

       关于next_permutation和prev_permutation

       在STL中next_permutation和prev_permutation是和排列相关的函数,前者是求出下一个排列组合,而后者是求出上一个排列组合。所谓上一个、下一个就是按照字典序排序的上一种、下一种排列,比如一个排列是acbd 那么下一个排列就是 acdb  上一个就是abdc。而abcd没有上一个排列,dcba没有下一个排列。


       函数实现的原理就是(next_permutation)在当前排列下,从右端找到一个元素*p,满足*p<*(p+1),那么遍历*p之后所有的元素,找到大于*p的最小元素*(p+n),将*p和*(p+n)替换内容,最后将*p之后的所有元素进行倒叙,得到的排列就是“下一个”排列。例如:对于127384这个排列,从右往左看,8>4不满足*p<*(p+1),而3<8,满足,这时候3右边的元素有8和4,4是大于3的最小元素,将3和4替换得到127483,最后4后面的元素倒序,得到127438,即为127384的下一个排列。  对于prev_permutation,操作的思想类似,先从右边找到一个数满足这个数大于右边紧邻的数,然后在它右边找到比它小的最大数,再替换倒叙即可。

 

        于是利用next_permutation,就能方便的进行字典序全排列了。只需要对初始序列排下序,再循环调用该函数,初始的排序不要忘记了,因为是全排列。


另外STL中next_permutation的函数原型为:

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类型。对于当前的地址内的序列如果不是最大字典序,进行下一个的排列,成功返回true,否则返回false。注意返回false同时会重新设置该排列为最小字典序。

代码示例:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50;

vector<int> arr(maxn);

int main()
{
    ios::sync_with_stdio(false);
    int n;
    int num=1;
    cin>>n;
    for(int i=1;i<=n;++i) arr[i]=i;
    sort(arr.begin()+1,arr.begin()+n+1);
    for(int i=1;i<=n;++i) cout<<arr[i]<<' ';
    cout<<endl;
    while(next_permutation(arr.begin()+1,arr.begin()+n+1))
    {
        num++;
        for(int i=1;i<=n;++i) cout<<arr[i]<<' ';
        cout<<endl;
    }
    cout<<num<<"个"<<endl;
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值