n个元素的所有子集(递归+非递归 +不去重)

算法 专栏收录该内容
82 篇文章 5 订阅

一、非递归方法
思路分析:n个元素的子集共有2^n个,其中包括空集。
(1)假设有3个元素{a, b, c},那么此时有 2^3 个子集,即8个子集。
(2)因为有8个子集,而且包括空集,注意7对应的二进制形式为111,并且二进制数刚好3位;所以(000 ~ 111)可分别对应一个子集,若该位为1,则表示该元素出现在子集中,若该位为0,则表示该元素不出现在子集中;
(3)注意:001中的1在低位,对应的是a,即数组中的首个元素。
(4)举例
111表示子集abc;
110表示子集bc;
101表示子集ac;
100表示子集c;
011表示子集ab
010表示子集b;
001表示子集a;
000则表示空集;
具体实现如下:

#include <iostream>
using namespace std;

typedef unsigned long DWORD; // DWORD 即double world,双字节。

// 求arr的子集,arr共有n个元素的所有子集,时间复杂度为2^n
void print_allSubSet(int arr[],int n)
{
    DWORD i,j,total,mask;
    if (n > 30)
    {
        printf("%d is too big\n",n);
        return ;
    }

    total= (1 << n);  // 1 << n 即把1的二进制形式,左移n位;因为2^n不好表达,所以采用移位的方式;(n个元素共有2^n个子集,包括空集)
    for (j=0; j < total; j++) // 每循环一次选出一个子集
    {
        printf("{ ");

        i = 0; // 每一次循环,i都重新置0;对应原数组中的第一个数字。
        mask = j; // 序号j对应的是第(j+1)个子集。
        while (mask > 0) // 通过移位的方式,直至mask的二进制形式中,所有位都为0。
        {
            if (mask & 1) // 若mask的二进制形式的最后一位非0,输出该位对应的数字。
                printf("%d ", arr[i]);
            mask >>= 1; // mask右移一位
            i++;
        }
        printf("}\n");
    }


}


int main(int argc, const char * argv[]) {

    int n=3;  //求3个元素的所有子集。
    int arr[32];
    int i;
    for (i=0;i<n;i++)
        arr[i]=i+1;  //arr表示一个集合,共有n个元素
    print_allSubSet(arr, n);

    return 0;
}

打印如下:
这里写图片描述

二、递归方法
思路分析:同上
此处,我们添加一个标记数组tag,用于记录子集中对应的元素是否出现。每输出一个子集,结束当前步骤,并进入下一步,直至递归完毕。
具体实现如下:

#include <iostream>
using namespace std;

// 递归
void allSubSet(int arr[], int tag[], int n)
{
    if(n == 3)
    {
        cout<< "{ ";
        for(int i = 0; i < 3; i++)
            if(tag[i] == 1)
                cout << arr[i] << ' ';
        cout<< "}" << endl;
        return;
    }
    tag[n] = 0;
    allSubSet(arr, tag, n+1);
    tag[n] = 1;
    allSubSet(arr, tag, n+1);
}

int main(int argc, const char * argv[]) {

    int a[3]={1,2,3};
    int tag[3];
    allSubSet(a,tag,0);    

    return 0;
}

输出如下:
这里写图片描述

知识拓展:
(1)n个元素的所有子集(递归+非递归+去重)
(2)求元素数量为定值的所有子集;求元素和为定值的所有子集;

  • 5
    点赞
  • 1
    评论
  • 8
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

小僧_

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值