枚举子集的三种方法~紫书

之前一直没怎么重视这一块,理解也是半知半解,现在想想还是得好好记下。

 

一.增量构造法

什么意思呢,简单来说就是把一个一个元素放进去又拿出来的过程

先上代码

 

//ass[]集合的数组
//
num[]子集的数组 //n 集合的元素的个数 //cur 初始为0,代表着子集的元素的个数 void print_subset(int n, int *num, int cur) { for (int i = 0; i < cur; i++) {//输出目前子集 cout << ass[num[i]]; } cout << endl; int st = cur ? num[cur - 1]+1 : 0;//选择最小的未被选择的集合的一个元素 for (int i = st; i < n; i++) { num[cur] = i;//放入数组代替前面的num[cur] print_subset(n, num, cur + 1); } }

下面借用一张图,思路很清晰

注意:这里的1 2 3 4表示的是集合的下标,不是元素的值

 

跟着算法的思路走一遍:

一开始,我们的子集为空,所以不需要输出,然后我们现在的子集元素个数cur为0,然后 我们拿   目前的  未被用到的  最小号元素放进去  子集,

OK,目前的状态是子集里面已经有了0号元素,保持这个状态我们接着往下走,

现在,我们的子集里面已经有了0号元素,可以输出0号,目前我们的子集元素个数cur为1,然后我们拿   目前的  未被用到的  最小号元素放进去  子集

OK,目前的状态是子集里面有了1号元素,保持这个状态我们接着往下走

………………………………以此类推,直到遇到第一个边界,也就是num[cur]==3 的时候,我们的集合的元素的个数只有4个,所以输出目前的子集后我们直接返回走,

目前我们回到了num[ cur==3 ]=3的状态,开始进入下一个循环,因为 i此时等于 4 ,不满足循环条件,接着返回上一个状态

目前我们回到了num[cur==2 ]=2的状态,开始进入下一个循环,

ok,此时我们的num[cur==2]=3,也就是说本来num[2]==2此时被代替成了3,这样子目前  子集就是  0 1 3,以此类推

这样我们的结果就是

0

0 1

0 1 2

0 1 2 3

0 1 3

0 2

0 2 3

0 3

1

1 2

1 2 3

1 3

2

2 3

3

 

二.位向量法

其实就是 放和不放 所得到的各种情况

例如 我目前的 集合的元素为 1 2 3 4 5

那我先拿出第一个元素  1

那我的子集要不要这个元素呢?有要和不要两种情况

那目前就有了两个子集  :   第一个子集 为   空

              第二个子集 为   1

再拿出第二个元素  2:

那我的子集要不要这个元素呢?有要和不要两种情况,而第一个元素 1 要和不要也有两种情况,于是目前就有了四种情况

那目前就有了四个子集:   第一个子集为空

              第二个子集为 1

              第三个子集为 2

                 第四个子集为 1 2

以此类推,我们就会得到所有子集的情况

下面给出代码:

#include<iostream>
using namespace std;

//ass[]集合的元素
//num[]子集的数组
//n 集合的元素的个数
//cur 初始为0,代表着集合元素的下标

void print_subset(int n, int *num, int cur)
{
    if (cur == n) {
        for (int i = 0; i < cur; i++) {
            if (num[i]) {
                cout << ass[i];
            }
        }
        cout << endl;
        return;
    }
    num[cur] = 1;//要ass[cur]这个元素
    print_subset(n, num, cur + 1);

    num[cur] = 0;//不要ass[cur]这个元素
    print_subset(n, num, cur + 1);
}

 

 

三  . 二进制法

这个应该是最难懂的,需要先学习二进制的概念和各种位运算符,不然没必要往下看

如果你已经懂了,那接着往下说。

 

要知道,二进制中,只有 1 和 0 两个数字,那我们就把 1代表有这个元素,0代表没有这个元素

 

 

假如我们的集合为{7 , 8 ,9}

那么子集用二进制表示就是这样

001 010 011 100 101 110 111 如此,我们知道有9种情况

这代表着什么呢?

 

(它的代表是反向的)

例如 001代表着 有 7 这个元素 ,没有 8 和 9 元素 ,

        101代表着 有7和 9元素,没有 8元素

  1 1 1 代表着全部都有

 

 

那好,我们知道它是怎么代表的,那又有什么用呢?

接着往下看:

首先我们要知道集合元素的个数,这里假设为 N

那我们再看这样一个式子    (1<<N)-1    

什么意思呢?我们知道数组是从0开始的,比如我们的集合有3个元素,放在数组里面,它们的下标就是 0 1 2

那么我们可以知道N==3,所以我们  (1<<N)-1==(1000 - 0001)(二进制)==111,看!“ 111 ”  那它不就代表着集合全部的元素都有吗!

那么我们接下来不断的让 111 减去 001也就是 十进制的 1 ,那么就有了   110   101  011  010 001  000

所以,下面给出枚举的代码:

//ass[]集合的元素
//n 集合的元素的个数

void print_subset(int n,int *ass)
{
    for (int i = (1 << n) - 1; i > 0; i--) {//子集目前的二进制状态
        for (int j = 1; j <= n; j++) {
            if (i&j) {//如果子集的二进制目前的j位为1,那就输出
                cout << ass[j - 1];
            }
        }
        cout << endl;
    }

 

全部评论

相关推荐

点赞 评论 收藏
分享
喜欢走神的孤勇者练习时长两年半:池是池,发是发,我曾池,我现黑
点赞 评论 收藏
分享
11-02 09:49
已编辑
货拉拉_测试(实习员工)
热爱生活的仰泳鲈鱼求你们别卷了:没事楼主,有反转查看图片
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务