1. 程式人生 > >求m個元素集合中n個元素的所有子集(C/OC)

求m個元素集合中n個元素的所有子集(C/OC)

演算法分析

假設有個集合擁有m個元素,任意的從集合中取出n個元素,則這n個元素所形成的可能子集有那些?

 假設有5個元素的集點,取出3個元素的可能子集如下:

{1 2 3}、{1 2 4 }、{1 2 5}、{1 3 4}、{1 3 5}、{1 4 5}、{2 3 4}、{2 3 5}、{2 4 5}、{3 4 5}

這些子集已經使用字典順序排列,如此才可以觀察出一些規則:

如果最右一個元素小於m,則如同碼錶一樣的不斷加1

如果右邊一位已至最大值,則加1的位置往左移

每次加1的位置往左移後,必須重新調整右邊的元素為遞減順序

所以關鍵點就在於哪一個位置必須進行加1的動作,到底是最右一個位置要加1?還是其它的位置?

在實際撰寫程式時,可以使用一個變數positon來記錄加1的位置,position的初值設定為n-1,因為我們要使用陣列,而最右邊的索引值為最大 的n-1,在position位置的值若小於m就不斷加1,如果大於m了,position就減1,也就是往左移一個位置;由於位置左移後,右邊的元素會 經過調整,所以我們必須檢查最右邊的元素是否小於m,如果是,則position調整回n-1,如果不是,則positon維持不變。

程式碼實現

#define MAX 20 

//主程式(註釋僅供參考)
int set[MAX];
int m, n, position;
int i;
m = 5;                          //集合元素總數 m
n = 3;                          //取出元素個數 n
for(i = 0; i < n; i++)
    set[i] = i + 1;
for(i = 0; i < n; i++)          //顯示第一個集合
    printf("%d ", set[i]);
putchar('\n');
position = n - 1;
while(1) {
    if(set[n-1] == m)           //如果元素值==m,標誌位置--
        position--;
    else
        position = n - 1;

    set[position]++;            //這個是必然的,position指向的位置數值+1
    
    for(i = position + 1; i < n; i++)   //調整右邊元素,position位置非n-1時,後面順序賦值
        set[i] = set[i-1] + 1;  //首元素+1也發生在這裡
    
    for(i = 0; i < n; i++)      //列印前n個元素
        printf("%d ", set[i]);
    putchar('\n');
    if(set[0] >= m - n + 1)
        break;
    }
}