现有一个包含N个元素的集合S,求集合S的所有子集?
例如:集合S包含三个元素{a, b, c},则它的所有子集为:{ }(空集), {a}, {b}, {c}, {a, b}, {a, c}, {b, c} 和{a, b, c}。
这里先用位操作的思路来求解,具体方法:用2进制Bit位来标记集合中的某个元素是否被选中,1代表选中,0代表未选中。例如集合{a, b, c}的所有子集可如下表示:
{}(空集) 0 0 0
{a} 0 0 1
{b} 0 1 0
{c} 1 0 0
{a, b} 0 1 1
{a, c} 1 0 1
{b, c} 1 1 0
{a, b, c} 1 1 1
从上面的分析中也可以看出一个包含N个元素的集合S有2^N个子集,非常容易想到的方法就是遍历0~2^N-1的所有整数,并转化为二进制,按以上思路输出所有子集。但这里我们参照《求比正整数N大的最小正整数M,且M与N的二进制表示中有相同数目的1》的方法来求解集合S的所有子集。具体实现如下:
#include <iostream>
using namespace std;
const int OK = 0;
const int ERROR = 1;
int getNextN(int n)
{
int temp1 = n & (-n);
int temp2 = n + temp1;
int ret = temp2 | ((n ^ temp2) / temp1) >> 2;
return ret;
}
template<class T>
void output(T set[], int k, int n)
{
int index = 0;
bool flag = false;
cout << "{";
while (n)
{
if (n % 2 == 1)
{
if (flag)
{
cout << ", ";
}
cout << set[k + index];
flag = true;
}
n /= 2;
index++;
}
cout << "}" << endl;
}
//生成集合set[k:m]的所有子集
template<class T>
int SubSet(T set[], int k, int m)
{
if (k < 0 || m < 0 || k > m)
{
return ERROR;
}
output(set, k, 0); //空集作特殊处理
int num = m - k + 1;
for (int i = 1; i <= num; i++) //从集合set[k:m]中选择i个元素
{
int beginIdx = (1 << i) - 1;
int endIdx = (1 << num) - (1 << (num - i));
for (int j = beginIdx; j <= endIdx;)
{
output(set, k, j);
j = getNextN(j);
}
}
return OK;
}
int main(int argc, char *argv[])
{
char str[10];
cout << "请输入一个字符数组:";
cin >> str;
SubSet(str, 0, strlen(str) - 1);
system("PAUSE");
return EXIT_SUCCESS;
}
这里再提供一种递归的思路来解决问题,会借助一个全局变量来记录所有选中的元素,具体代码如下:
#include <iostream>
using namespace std;
int g_used[10] = {0};
template<class T>
void Output(T set[])
{
bool flag = false;
cout << "{";
for (int i = 0; i < 10; i++)
{
if (g_used[i] == 1)
{
if (flag)
{
cout << ", ";
}
cout << set[i];
flag = true;
}
}
cout << "}" << endl;
}
//生成集合set[k:m]的所有子集
template<class T>
void SubSet(T set[], int k, int m)
{
if (k > m)
{
return Output(set);
}
g_used[k] = 0; //未选中
SubSet(set, k + 1, m);
g_used[k] = 1; //选中
SubSet(set, k + 1, m);
}
int main(int argc, char *argv[])
{
char str[10];
cout << "请输入一个字符数组:";
cin >> str;
SubSet(str, 0, strlen(str) - 1);
system("PAUSE");
return EXIT_SUCCESS;
}