枚舉全部子集的三種算法詳解-《算法入門經典》

方法一:增量構造法數組

  理解遞歸必須得理解函數究竟是作什麼的。函數

#include<cstdio>

void print_subset(int n,int *a,int cur)  //print_subset的功能是打印集合{0,1,...,n-1}的cur個元素的子集,而且目前a數組中已經存儲了要打印的集合 
{
    printf("{ ");
    for (int i=0;i<cur;i++) printf("%d ",a[i]);
    printf("}\n");
    int mi=cur?a[cur-1]+1:0;  //利用字典序避免重複打印 
    for (int i=mi;i<n;i++)  //枚舉下一位能夠添加什麼元素 
    {
        a[cur]=i;
        print_subset(n,a,cur+1);  //遞歸打印cur+1個元素的子集 
    }
}

int main()
{
    int n;
    int a[10]={};
    scanf("%d",&n);
    print_subset(n,a,0); //初始時a中存儲了0個元素的子集,開始遞歸打印 
    return 0;
}

 

方法二:位向量法spa

  枚舉每一位選或者不選,複雜度比方法一略高但更好理解,由於與輸出全排列思路差很少,滿n位就輸出。code

#include<cstdio>

int a[10];

void print_subset(int n,int *b,int cur)  //肯定第cur位選或者不選 
{
    if (cur==n)  //若是肯定好了n位,打印結果 
    {
        printf("{ ");
        for (int i=0;i<n;i++)
            if (b[i]) printf("%d ",a[i]);
        printf("}\n");
    } 
    else
    {
        b[cur]=1;  //這一位選 
        print_subset(n,b,cur+1);  //遞歸下一位 
        b[cur]=0;  //這一位不選 
        print_subset(n,b,cur+1);  //遞歸下一位 
    }
}

int main()
{
    int n;
    int b[10]={};
    scanf("%d",&n);
    for (int i=0;i<n;i++) a[i]=i;
    print_subset(n,b,0);
    return 0;
}

  缺點是輸出不是按照字典序。blog

 

方法三:二進制法遞歸

  稍加思考就會發現,方法二其實與二進制是對應的。io

#include<cstdio>

int a[10];

void print_subset(int n,int b)  //n位的集合,b狀態打印 
{
    printf("{ ");
    for (int i=0;i<n;i++)
        if (b&(1<<i)) printf("%d ",a[i]);
    printf("}\n");
}

void p_s(int n)
{
    for (int i=0;i<(1<<n);i++) print_subset(n,i);  //枚舉全部狀態 
}

int main()
{
    int n;
    scanf("%d",&n);
    for (int i=0;i<n;i++) a[i]=i;
    p_s(n);
    return 0;
}

  這個方法優勢就是代碼簡單。class

 

注意:以上三種方法輸出順序不一樣。二進制

相關文章
相關標籤/搜索