組合演算法面試題

來源:互聯網
上載者:User

組合演算法題往往有多個變種,如求一個集合的全部子集以及部分組合問題,這篇文章對這些常見的面試題做個匯總,權當做個記錄,以免自己哪天忘了,難得到網路上去找。

一、求一個集合的全部子集

題目:給定一個集合s={a, b, c, d},試給出一個演算法輸出該集合的除了空集之外的全部子集。

分析:我們知道,一個集合的子集數目跟它的元素數目有關,集合元素數目為n,則子集數目為2^n。如包含兩個元素的集合s1 = {a, b},則它的子集有:空集、{a}、{b}、{a, b}一共四個。求子集問題即從包含n個元素的集合中選取m個元素的問題,m可以是1...n。我們先從頭掃描字串的第一個字元。針對第一個字元,我們有兩種選擇:一是把這個字元放到組合中去,接下來我們需要在剩下的n-1個字元中選取m-1個字元;二是不把這個字元放到組合中去,接下來我們需要在剩下的n-1個字元中選擇m個字元。這兩種選擇都很容易用遞迴實現。實現代碼如下:

void Combination(char* string){    if(string == NULL)        return;    int length = strlen(string);    vector<char> result;    for(int i = 1; i <= length; ++ i)    {        Combination(string, i, result); //從strng中選擇i個字元,結果儲存在result中    }} void Combination(char* string, int number, vector<char>& result){    if(number == 0)    {        vector<char>::iterator iter = result.begin();        for(; iter < result.end(); ++ iter)            printf("%c", *iter);        printf("\n");        return;    }    if(*string == '\0')        return;    result.push_back(*string); //選擇當前字元    Combination(string + 1, number - 1, result);    result.pop_back();  //不選當前字元    Combination(string + 1, number, result);}

求子集還有一個更簡單的演算法,就是使用二進位。將數字從1——2^n-1迴圈,哪個位置位就選擇。假定集合字元為{a, b ,c },則

001           a

010           b

011           a       b

...

依次類推就可。代碼如下:

void comb(char *str) {    int len = strlen(str); //字串長度,如str = “abc”長度為3    int max = 1 << len; //字串自己的數目,如"abc"子集數目為8    for (int i=1; i<max; i++) {  //輸出所有子集,這裡除去空集,所以從i=1開始輸出        int k = i;        int index = 0;        while (k > 0) {            if (k & 1) {                 cout << str[index] << " ";            }            k >>= 1;            index++;        }        cout << endl; //每次輸出一個子集換行    }}

二、人民幣問題

題目:人民幣有1元、2元、5元、10元、20元、50元、100元面值,試給出一個演算法找出和為100的人民幣組合(不包括100本身)。比如2張50的,或者2張50+2張20+1張10等。

分析:該題目與上面問題類似,可以採用組合的思路來解決。代碼如下:

/* * rmb.cpp * *  Created on: 2012-8-28 *      Author: shusheng */#include <iostream>using namespace std;#define N 6int w[N];int number_used[N];bool is_used[N];int countnum = 0;void init() {w[0] = 1;w[1] = 2;w[2] = 5;w[3] = 10;w[4] = 20;w[5] = 50;for (int i = 0; i < N; i++) {number_used[i] = 0;}}void rmb(int start_index, int left_weight) {if (left_weight == 0) {for (int i = 0; i < N; i++) {if (number_used[i] > 0)cout << w[i] << "元: " << number_used[i] << "張 ";}cout << endl;countnum++;return;}for (int i = start_index; i < N; i++) {if (left_weight >= w[i]) {number_used[i]++;rmb(i, left_weight - w[i]);number_used[i]--;}}}int main() {init();rmb(0, 100);cout << countnum << endl;return 0;}

函數rmb(start_index, left_weight)的功能定義是從start_index開始選擇,輸出最終和為left_weight的所有錢幣組合。這裡類似於完全背包問題,即每一樣錢幣都可以選擇多次,而背包容量大小為100,每樣物品的價值就是錢幣面值,只是這裡不是求最大值,而是總的組合數目。

當然這裡的代碼可以修改成另外一種形式,也許更好理解,如下所示:

void rmb(int start_index, int left_weight){    if (left_weight == 0)    {        for (int i = 0; i < N; i++)        {            if (number_used[i] > 0) cout << w[i] << "元: "<< number_used[i] <<"張 ";        }        cout << endl;        return;    }    for (int i = start_index; i < N; i++)    {        int y = left_weight;        while (y >= w[i]) {            number_used[i]++;            y -= w[i];            rmb(i+1, y);        }        number_used[i] = 0;    }}

這裡的代碼for迴圈中就是先從1元開始選,這種情況完成後,再從5元開始選(即最小錢幣值為5),再是10、20等。
三、整數分解問題

給定一個正整數,試輸出所有的分解。如5=1+1+1+1+1 = 1+1+1+2=1+1+3=1+2+2=1+4

其實這個問題也可以參照上面的人民幣的例子,只是這裡的數組取值改成了1,、2、3、4...n-1。當然此題應該還有更好的解法,暫時以這個思路寫一下:

#include <iostream>#include <vector>using namespace std;#define NUMBER 10  //要分解的數為10static int cnt = 0; //分解數目vector<int> part; //用於儲存分解結果void generate_partition(int x, int i, int v[]){    if (x == 0) {  //輸出        cout << ++cnt << ": ";        for (int j=0; j<part.size(); j++) {            cout << part[j] << " ";        }        cout << endl;        return;    }    for (int j=i; j<NUMBER-1; ++j) { //輸出邏輯是先輸出包含1個v[j]的,然後是2個v[j]的...        int select = v[j];        int c = 0, y=x;        while (y >= select) {             part.push_back(select);            y -= select; c++;            generate_partition(y, j+1, v);        }        while (c--)            part.pop_back();    }}int main(){    int x = NUMBER;    int v[NUMBER-1];    for (int i=0; i<NUMBER-1; i++)        v[i] = i+1;    generate_partition(x, 0, v);    return 1;}

參考資料

http://zhedahht.blog.163.com/blog/static/2541117420114172812217/

http://blog.csdn.net/yysdsyl/article/details/4215232

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.