1. 程式人生 > >枚舉子集的三種方法~紫書

枚舉子集的三種方法~紫書

ace span 遇到 應該 print 增量 hellip 等於 ret

之前一直沒怎麽重視這一塊,理解也是半知半解,現在想想還是得好好記下。

一.增量構造法

什麽意思呢,簡單來說就是把一個一個元素放進去又拿出來的過程

先上代碼

//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;
    }

枚舉子集的三種方法~紫書