已知一个集合,其子集包括空集和本身。怎样将所有的子集打印出来?最简单的O(N^3)算法不难想到,但是太过于朴素,应该还有更巧的方法。
我们已知,一个元素个数为n的集合,其子集个数为2^n个。比如set { 1, 2 },含有两个元素,一共有四个子集,分别为{ },{ 1 },{ 2 },{ 1,2 }。 我们取一个只有两位的bitset,显然这个两位的bitset只有四种组合,00、01、10、11。 假设我们让bitset的每一位对应上面集合中的一个元素,那么集合的每个子集就可以用bitset的一个组合来表示:
{ } 对应 00
{ 1 } 对应 01
{ 2 } 对应 10
{ 1,2 } 对应 11
在这个对应关系中,bitset的某一位为1,则代表其对应的集合中的元素选入这个子集,为0则代表不选入。
扩展开,一个具有n个元素的集合,可以用n位的bitset来表示, 将n位的bitset转换为十进制,其范围刚好就是【0,2^n)。上面的特例,bitset转换为十进制后,分别为0、1、2、3。那么,打印一个集合的子集,我们只需要遍历从0到2^n-1的数字,将每个数字看作bitset,只要某位为1则输出该位对应元素。
根据这个思想,形成代码:
//felix:算法思想, {a,b,c,d...) 与二进制的0x11111..每一位互相对应. 形成映射. 相当于一个元素代表一个数。总共会有2^n-1个数,算法是遍历各个数,将一个总和拆成几个基本数的连加.
- #include <iostream>
- #include <math.h>
- using namespace std;
- void Print(int set[], int n, int k)
- {
- if (k==0)
- {
- cout <<"空集";
- return;
- }
- int mask = 1;
- int index = 0;
- while (index < n)
- {
- //mask 1, 2,4,8...,如果k是0x11,则mask=1,2则&出来1,print出来
- if (k&mask)
- {
- cout << set[index] << " ";
- }
- mask <<= 1;
- index++;
- }
- }
- void PrintSubSet(int set[], int n)
- {
- int i;
- for (i=0; i<pow(2,n); i++)
- {
- Print(set, n, i);
- cout << endl;
- }
- }
- void main()
- {
- int set[4] = {1,2,3,4};
- PrintSubSet(set, 4);
- }
输出结果: