http://hawstein.com/posts/8.3.html
原文:
Write a method that returns all subsets of a set.
译文:
写一个函数返回一个集合中的所有子集。
解答
对于一个集合,它的子集一共有2n 个(包括空集和它本身)。它的任何一个子集,我们都可以理解为这个集合本身的每个元素是否出现而形成的一个序列。比如说,对于集合{1, 2, 3},空集表示一个元素都没出现,对应{0, 0, 0};子集{1, 3},表示元素2没出现(用0表示),1,3出现了(用1表示),所以它对应 {1, 0, 1}。这样一来,我们发现,{1, 2, 3}的所有子集可以用二进制数000到111 的8个数来指示。泛化一下,如果一个集合有n个元素,那么它可以用0到2n -1 总共2n 个数的二进制形式来指示。每次我们只需要检查某个二进制数的哪一位为1,就把对应的元素加入到这个子集就OK。代码如下:
typedef vector<vector<int> > vvi;
typedef vector<int> vi;
vvi get_subsets(int a[], int n){ //O(n2^n)
vvi subsets;
int max = 1<<n;
for(int i=0; i<max; ++i){
vi subset;
int idx = 0;
int j = i;
while(j > 0){
if(j&1){
subset.push_back(a[idx]);
}
j >>= 1;
++idx;
}
subsets.push_back(subset);
}
return subsets;
}
解这道题目的另一种思路是递归。这道题目为什么可以用递归?因为我们能找到比原问题规模小却同质的问题。比如我要求{1, 2, 3}的所有子集,我把元素1拿出来,然后去求{2, 3}的所有子集,{2, 3}的子集同时也是{1, 2, 3} 的子集,然后我们把{2, 3}的所有子集都加上元素1后,又得到同样数量的子集,它们也是{1, 2, 3}的子集。这样一来,我们就可以通过求{2, 3}的所有子集来求 {1, 2, 3}的所有子集了。而同理,{2, 3}也可以如法炮制。代码如下:
vvi get_subsets1(int a[], int idx, int n){
vvi subsets;
if(idx == n){
vi subset;
subsets.push_back(subset); //empty set
}
else{
vvi rsubsets = get_subsets1(a, idx+1, n);
int v = a[idx];
for(int i=0; i<rsubsets.size(); ++i){
vi subset = rsubsets[i];
subsets.push_back(subset);
subset.push_back(v);
subsets.push_back(subset);
}
}
return subsets;
}
完整代码如下:
#include <iostream>
#include <vector>
using namespace std;
typedef vector<vector<int> > vvi;
typedef vector<int> vi;
vvi get_subsets(int a[], int n){ //O(n2^n)
vvi subsets;
int max = 1<<n;
for(int i=0; i<max; ++i){
vi subset;
int idx = 0;
int j = i;
while(j > 0){
if(j&1){
subset.push_back(a[idx]);
}
j >>= 1;
++idx;
}
subsets.push_back(subset);
}
return subsets;
}
vvi get_subsets1(int a[], int idx, int n){
vvi subsets;
if(idx == n){
vi subset;
subsets.push_back(subset); //empty set
}
else{
vvi rsubsets = get_subsets1(a, idx+1, n);
int v = a[idx];
for(int i=0; i<rsubsets.size(); ++i){
vi subset = rsubsets[i];
subsets.push_back(subset);
subset.push_back(v);
subsets.push_back(subset);
}
}
return subsets;
}
void print_subsets(vvi subsets){
for(int i=0; i<subsets.size(); ++i){
vi subset = subsets[i];
for(int j=0; j<subset.size(); ++j){
cout<<subset[j]<<" ";
}
cout<<endl;
}
}
int main(){
int a[] = {
1, 2, 3, 4
};
vvi sub = get_subsets(a, 4);
vvi sub1 = get_subsets1(a, 0, 4);
print_subsets(sub);
print_subsets(sub1);
return 0;
}
全书题解目录:
Cracking the coding interview—问题与解答
PS : 全排列
#include <stdio.h>
void perm(int* data, int n, int curr)
{
if (curr==n-1)
{
for (int i = 0; i < n; ++i)
printf("%d", data[i]);
printf("\n");
}
else
{
for (int i = curr; i < n; ++i)
{
int t;
t = data[curr], data[curr] = data[i], data[i] = t;
perm(data, n, curr+1);
t = data[curr], data[curr] = data[i], data[i] = t;
}
}
}
int main()
{
int array[128] = {0};
int n = 0, i = 0;
scanf("%d", &n);/*没有数据合法性检查*/
for (i = 0; i < n; ++i) array[i] = i+1;
perm(array, n, 0);
return 0;
}
4
1234
1243
1324
1342
1432
1423
2134
2143
2314
2341
2431
2413
3214
3241
3124
3142
3412
3421
4231
4213
4321
4312
4132
4123
Press any key to continue