[經典算法] 排列組合-N元素集合的所有子集(一)


題目說明:

給定一組數字或符號,產生所有可能的集合(包括空集合),例如給定1 2 3,則可能的集合為:{}、{1}、{1,2}、{1,2,3}、{1,3}、{2}、{2,3}、{3}。

 

題目解析:

如果不考慮字典順序,則有個簡單的方法可以產生所有的集合,思考二進位數字加法,並注意1出現的位置,如果每個位置都對應一個數字,則由1所對應的數字所產生的就是一個集合,例如:

000 {}
001 {3}
010 {2}
011 {2,3}
100 {1}
101 {1,3}
110 {1,2}
111 {1,2,3}

了解這個方法之后,剩下的就是如何產生二進位數?有許多方法可以使用,您可以使用unsigned型別加上&位元運算來產生;

如果是32個以內的元素集合可以采用unsigned int來儲存,這樣直接遍歷再根據比特位顯示出元素就可以了。

比如3個元素,則對應最大值是2^3 = 8;

for (int i=0; i < 8; i++)
     ShowResult(&i, 3); //根據bit位顯示結果

這里我假定任意個元素集合求子集合,通過數組來表示任意長位數;

 

程序代碼:

#include <gtest/gtest.h>
using namespace std;

void ShowResult(bool Bits[], int nSize)
{
    cout << "{";
    for (int i=0; i<nSize; ++i)
    {
        if (Bits[i])
        {
            cout << i+1 << " ";
        }
    }
    cout << "}\n";
}

bool Add(bool Bits[], int nSize)
{
    for (int i = nSize -1; i >= 0; --i)
    {
        Bits[i] = !Bits[i];    // 如果是1變成0再進位,如果是0變成1退出。
        if (Bits[i])
        {
            return true;            
        }    
    }

    return false;
}

// 二進制法
int GenerateSubset(int nSize)
{    
    if (nSize==0)
    {    
        cout << "{}" << endl;
        return 1;
    }
    
    int nCount = 0;
    bool *Bits = new bool[nSize];
    memset(Bits, false, sizeof(bool)*nSize);

    do
    {
        ShowResult(Bits, nSize);
        nCount++;
    }
    while(Add(Bits, nSize));

    delete[] Bits;

    return nCount;
}

TEST(Algo, tCombination)
{
    // 0個數子集合數 =〉2^0 = 1
    ASSERT_EQ(GenerateSubset(0), 1);

    // 3個數子集合數 =〉2^3 = 8
    ASSERT_EQ(GenerateSubset(3), 8);

    // 5個數子集合數 =〉2^5 = 32
    ASSERT_EQ(GenerateSubset(5), 32);

    // 10個數子集合數 =〉2^10 = 1024
    ASSERT_EQ(GenerateSubset(10), 1024);
}

 

參考引用:

根據組合數和二項式定理

子集個數:Cn0+Cn1+Cn2+...+Cnn = (1+1)^n=2^n

 

Book1  看書、學習、寫代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM