全排列、子集

来源:互联网 发布:淘宝网韩版女装秋季 编辑:程序博客网 时间:2024/05/21 17:30

问题

输出数组P[a,b,b,b]的全排列\全子集 ; 注意集合元素有重复

测试

#define N 4char P[]={'a', 'b', 'b', 'c'}int main(){    int i, j, A[N];    printf("全排列-递归\n");    permutation1(N, A, 0);    printf("全排列-非递归\n");    //用索引 转化数组P 为A[1, 2, 2, 4]    for(i=0; i<N; i++)    {        if( i==0 || P[i]!=P[i-1] )            j = i;        A[i]=j;    }    permutation2(N, P);    printf("子集--增量构造--没有去重\n");    subset1(N, A, 0);    printf("子集--位向量--没有去重\n");    subset2(N, A, 0);    printf("子集--二进制--去重啦\n");    //用索引 转化数组P 为A[1, 2, 2, 4]    for(i=0; i<N; i++)    {        if( i==0 || P[i]!=P[i-1] )            j = i;        A[i]=j;    }    memset(HashArray, -1, sizeof(HashArray));    subset3(N, A);}输出:全排列-递归a b b b b a b b b b a b b b b a 全排列-非递归a b b b b a b b b b a b b b b a 子集--增量构造--没有去重a a b a b b a b b b a b b a b a b b a b b b b b b b b b b b b b 子集--位向量--没有去重b b b b b b b b b b b b a a b a b a b b a b a b b a b b a b b b 子集--二进制--去重啦a b a b b b a b b b b b a b b b 

全排列

递归算法

/* 递归 * 不断的填充索引A[cur]的值 直到cur==n */void permutation1(int n, int *A, int cur){    int i,j,c1,c2;    if(cur==n)    {        for(i=0; i<n; i++)        {            printf("%c ", P[A[i]]);        }        printf("\n");        return;    }    for(i=0;i<n;i++) if( i==0 || P[i]!=P[i-1] )    {        c1=c2=0;        for(j=0;j<cur;j++) if(P[i]==P[A[j]]) c1++; // 已经出现次数        for(j=0;j<n && c1>=c2; j++)   if(P[i]==P[j]) c2++; // 是否至少出现了c1+1次        if( c1 < c2 )        {            A[cur]=i;            permutation1(n, A, cur+1);        }    }}

非递归算法

/* 非递归 */void permutation2(int n, int *A){    int i, j;    /*     * 从字典序最小[升序排列] 逐步增大 直到 字典序最大[降序排列]     * 算法:     *  初始态 值升序排列     *  从右往左找到i  使得 A[i] > A[i+1] && A[i+1:]降序  < i==-1 A[0:] 已经是降序排列了 算法结束 >     *  从右往左找到j  使得 A[i] < A[j];     *  逆序 A[i+1:]     */    while(1)    {        //打印        for(i=0; i<n; i++)            printf("%c ", P[A[i]]);        printf("\n");        //find 临界点A[i]        i=n-2;        while( i>=0 && A[i]>=A[i+1]) i--;        if(i==-1) return;        //find 比A[i]大的数字A[j]        j=n-1;        while(A[i]>=A[j]) j--;        // exchange        A[i] ^= A[j]; A[j] ^= A[i]; A[i] ^= A[j];        // reversed A[i+1:]        for(i=i+1, j=n-1; i<j; i++,j--)        { A[i] ^= A[j]; A[j] ^= A[i]; A[i] ^= A[j]; }    }}

子集

子集–增量构造

/* 子集--增量构造发 */void subset1(int n, int *A, int cur ){    int i, s;    for(i=0; i<cur; i++) printf("%c ", P[A[i]]);    printf("\n");    s = cur ? A[cur-1]+1 : 0;    for(i=s; i<n; i++)    {        A[cur]=i;        subset1(n, A, cur+1);    }}

子集–位向量

void subset2(int n, int *A, int cur){    int i;    if(cur==n)    {        for(i=0; i<n; i++) if(A[i])            printf("%c ", P[i]);        printf("\n");        return;    }    A[cur]=0;    subset2(n, A, cur+1);    A[cur]=1;    subset2(n, A, cur+1);}

子集–二进制

/* 判断n是否出现过 需要初始化memset(HashArray, -1, sizeof(HashArray)) * HashArray大小应该要大于所有无重复子集的个数 * */static int  HashArray[1024];int hasExisted(int n){    int LEN=1024;    int k = n%LEN;    while( HashArray[k]!=-1 && HashArray[k]!=n && k!=(n%LEN-1)%LEN) k=(k+1)%LEN;    if (k==(n%LEN-1)%LEN){        return 2; // 数据满了    }    if (HashArray[k]==-1) // 没有n    {        HashArray[k]=n;        return 0;    }    return 1;}/*子集--二进制*/void subset3(int n, int *A){    int i=0, j=0, code;    while(i < 1<<n)    {        code=1;        for(j=0; j<n ; j++) if( i & 1<<j )            code = code*10+A[j];        switch( hasExisted(code) )        {        case 1:            break;        case 2:            printf("数据error!\n");             return;        case 0:            for(j=0; j<n ; j++) if( i & 1<<j )                printf("%c ", P[A[j]]);            printf("\n");            break;        }        i++;    }}
原创粉丝点击