zoukankan      html  css  js  c++  java
  • 非负整数可重集去重&排序+获得可重集的全排列的几种方法

    非负整数可重集O(n)去重并排序

    可重集是指元素可重复的集合,对于在一定区间内的正整数集,比如[1,n],我们可以在不不使用任何额外空间(包括不使用O(1)的空间)的情况下,用O(n)的时间复杂度完成集合的去重并排序,这种O(n)的算法,是理想的联机算法。

    思路:本质上和桶排序类似,用数组下标来表示存在的元素,数组中的元素作为flag。

    对于正整数可重集来说,打标记的方法可以是将元素变负(思考,为什么不是随便赋一个规定的值),负整数依次类推。

    对于元素属于[1,n]的集合(n为元素个数),我们可以用下面的代码完成上述操作并取出元素,总时间是O(2n)

     1 void removeDuplicates(int *a,int len){
     2     for(int i=0;i<len;i++)
     3         a[abs(a[i])-1] = -abs(a[abs(a[i])-1]);//如果元素a[i]存在,则将a[abs(a[i])-1] 变负,下标0 ~ n-1 对应1 ~ n 
     4     for(int i=0;i<len;i++)if(a[i]<0)//如果a[i]<0 ,则说明i+1存在 ,取出 
     5         printf("%d ",i+1);
     6 }
     7 int main(){
     8     int seq[] = {5,3,4,2,3,1,2,2,5};
     9     removeDuplicates(seq,sizeof(seq)/sizeof(int));
    10      return 0;
    11 }

    获得可重集全排列:

    自己玩:

    可重集是指元素可重复的集合,可重集的全排列通常可以递归地进行求解。

    对于n个元素不重复的集合来说,我们可以递归为:

    1. 将第k个元素(k=1,2...n)放到集合首部
    2. 求解剩下n-k个元素的集合的全排列
    3. 重复1和2,直到集合的元素为空时,打印整个集合

    实现的代码(此处是以字符串为例),其中len表示字符串s的长度。注意,这里s必须定义为数组,如果定义为指针,将会引发错误,具体请看我的另一篇博客:C++指针和数组的区别中的情况2

     1 void swap(char &i,char &j){
     2     char t=i;i=j;j=t;
     3 }
     4 void permutation(char s[],int left,int len){
     5     if(left==len)printf("%s
    ",s);
     6     else{
     7         for(int k=left;k<len;k++){
     8             if(s[left]!=s[k])swap(s[left],s[k]);
     9             //递归求解n-k个元素的全排列
    10             permutation(s,left+1,len);
    11             if(s[left]!=s[k])swap(s[left],s[k]);
    12         }
    13     }
    14 }

    需要注意:这种实现不是遵从字典序的实现

    当我们需要打印可重集的全排列时,我们只需对递归调用的部分稍作改动

    1. 重复的情况要保证出现,所以,当left==k的时候,代表第一次递归,此时,应当保留
    2. 除了1之外,如果s[left]和s[k]仍有相等情况,则不应交换和递归,因为此时若递归,会造成重复

    简单修改上述代码,实现如下:

     1 void swap(char &i,char &j){
     2     char t=i;i=j;j=t;
     3 }
     4 void permutation(char s[],int left,int len){
     5     if(left==len)printf("%s
    ",s);
     6     else{
     7         for(int k=left;k<len;k++){
     8             //增加了上文的两个判定条件 
     9             if(k==left||s[left]!=s[k]){
    10                 swap(s[left],s[k]);
    11                 permutation(s,left+1,len);
    12                 swap(s[left],s[k]);
    13             }
    14         }
    15     }
    16 }

    同样,这种实现不是遵从字典序的实现

    当然,我们很多时候都需要按照字典序进行排列,说实话,我觉得我是很讨厌手写这个的,毕竟相当的麻烦,所以,就有了下面这个:

    黑科技:STL中的next_permutation(s,s+n)

    1 #include<algorithm>
    2 using namespace std;
    3 void permutation(char s[],int len){
    4     sort(s,s+len);//一定要先排序
    5     do{
    6         puts(s);
    7     }while(next_permutation(s,s+len));
    8 }

    这是货真价实的字典序的全排列,今天就到这,拜拜~

  • 相关阅读:
    程序设计实习课(0)资源链接
    解决clion2016.3不能支持搜狗输入法的问题
    四元数运动学笔记(5)IMU驱动的运动误差方程
    四元数运动学笔记(4)旋转的雅克比矩阵
    四元数运动学笔记(3)四元数和旋转相关的约定表述
    四元数运动学笔记(2)旋转向量,旋转矩阵和四元数的关系
    单应矩阵,基本矩阵,本质矩阵
    ROS标定IDS相机
    四元数运动学笔记(1)旋转的表示
    IMU Noise Model
  • 原文地址:https://www.cnblogs.com/luruiyuan/p/5616338.html
Copyright © 2011-2022 走看看