码迷,mamicode.com
首页 > 其他好文 > 详细

STL中的全排列实现

时间:2020-02-22 19:46:41      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:fine   stl   set   auth   names   main   存在   设计   int   

permutation:

在遇到全排列问题时,在数据量较小的情况下可以使用dfs的做法求得全排列,同时我们也知道在STL中存在函数next_permutation和prev_permutation,这两个函数可以较快的求出全排列,而这两个函数的实现却不是依赖于搜索算法(dfs)的。

分析:

以next_permutation为例,数据以1,2,3,4,5为例,对于一个排列我们知道其按照从小到大排序后的结果就是字典序最小的一个排列,而对于它的下一个排列为1,2,3,5,4比较这两个排列我们发现,排列的前三位没有改变,而最后两位发生了交换,原因是,对于一个排列而言他的从大到小排列(以下称倒序)为最后一个,因此对于倒序的排列,不存在下一个排列,而对于非倒序排列,一定存在下一个排列,因此对于1,2,3,4,5一定存在下一个排列。同时,为了找到他的下一个最小排列,我们应该尽可能保证前面的元素不变,改变最短的子序列达到下一个排列,所以我们从后向前找到第一个不满足逆序的位置即为4,之后,我们只需要找到在这个位置(这里是4)以后的这个子序列的下一个排列就可以了,4,5的下一个排列为5,4所以下一个排列为1,2,3,5,4。第三个排列为1,2,4,3,5。同之前的方法我们发现我们要寻找3,5,4的下一个排列,但是如果此时我们只是交换3和4的位置得到的4,5,3并不是他的下一个紧邻排列,我们观察发现,这样交换后,我们的到了4之后的序列的最大排列,但是因为我们的交换此时这个4处已经比上一排列的相同位置大了,而为了得到紧邻排列,我们需要将之后的子序列变换至最小排列,由最大排列变换至最小排列,我们只需要倒转这个序列就可。因此总结下来,从一个序列得到他的下一个紧邻排列,需要做:

  1. 从后向前查找第一个不满足倒序的位置ad
  2. 从后向前查找第一个大于ad处值的位置ch
  3. 交换ad和ch处的值
  4. 将ad+1到n的这个子序列倒转

代码实现:

#include<cstdio>
#include<cstring>

void inline swap(char *s1,char *s2){
    char t=*s1;
    *s1=*s2;
    *s2=t;
}

void reverse(char *s,char* e){//反转s到e的子序列
    for(e--;s<e;s++,e--)swap(s,e);
}

bool next_permutation(char *start,char *end){
    char *cur = end-1, *pre=cur-1;
    while(cur>start && *pre>=*cur)cur--,pre--;//找到第一个不满足逆序的位置
    if(cur<=start)return false;
    
    for(cur=end-1;*cur<=*pre;cur--);//找到逆序中大于*pre的元素的最小元素 
    swap(cur,pre);
    reverse(pre+1,end);//将尾部的逆序变成正序 
    return true;
}

int main(){
    char s1[]="01224",s2[]="8000";
    reverse(s1,s1+strlen(s1));
    printf("%s\n",s1);
    int n=strlen(s2);
    puts("下一个排列:");
    int cnt=0;
    do{
        puts(s2);
        cnt++;
    }while(next_permutation(s2,s2+n));
    printf("%d",cnt);
}

例题:有重复元素的排列问题

题目描述:

设R={ r 1 , r 2 , …, r n }是要进行排列的n个元素。其中元素r 1 , r 2 , …, r n 可能相同。试设计一个算法,
列出R的所有不同排列。给定n 以及待排列的n 个元素。计算出这n 个元素的所有不同排列。

输入:

第1 行是元素个数n,1≤n≤500。接下来的1 行是待排列的n个元素。

输出:

计算出的n个元素的所有不同排列输出到文件perm.out中。文件最后1行中的数是排列总数。

样例输入:

4
aacc

样例输出:

aacc
acac
acca
caac
caca
ccaa
6

题解:

本题主要不要考虑重复元素,只需要改变实现代码中的比较符号即可。

/**********************************************************
* @Author:             Maple
* @Date:               2020-02-22 17:47:36
* @Last Modified by:   Maple
* @Last Modified time: 2020-02-22 18:52:34
* @Remark: 
**********************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;

const int maxn=1000;
int n;
char str[maxn];

void swap(char &x,char &y){
    char temp=x;
    x=y;
    y=temp;
    return;
}

void reverse(int l){
    int i=0;
    while(l+i<n-i){
        swap(str[l+i],str[n-i]);
        i++;
    }
    return;
}

bool next_permutation(){
    int ad=n-1;
    //找到第一个不为逆序的元素
    for(ad;str[ad]>=str[ad+1];ad--);
    if(!(ad>0))
        return false;
    int ch=n;
    //找到第一个
    for(ch;str[ch]<=str[ad];ch--);
    swap(str[ad],str[ch]);
    reverse(ad+1);
    return true;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    #endif
    cin>>n;
    cin>>str+1;
    sort(str+1,str+1+n);
    int ans=0;
    do{
        puts(str+1);
        ans++;
    }while(next_permutation());
    cout<<ans<<endl;
    return 0;
}

STL中的全排列实现

标签:fine   stl   set   auth   names   main   存在   设计   int   

原文地址:https://www.cnblogs.com/LeafLove/p/12346737.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!