输出集合{1,2,...,n}的所有子集

来源:互联网 发布:什么软件可以涂鸦照片 编辑:程序博客网 时间:2024/05/24 15:41

输出集合{1,2,…,n}的所有子集

注意,集合{1,2,…n}与任何一个含有n个元素的集合是同构的,所有下面的算法适用于求解任何的集合。

基于完全二叉树的深度优先搜索

以集合{1,2,3}为例:

这里写图片描述

基于bit pattern

对于一个含有n个元素的集合,可以用一个n位的2进制数来表这个集合,第i为1表示第i个元素在表示的集合中,否则不在 。
[对一个数加上1,体现在bit位上的效果就是从低位向高位寻找,找到第一个0 bit位之后,将这个bit位置1,并将他后面的所有低位置0]
[顺便提一下,对一个数减1,体现在bit位上的效果就是从低位向高位寻找,找到第一个1 bit位之后,将这个bit位置0,并将他后面的所有低位都置1—对应的题可参见【编程之美】]

按字典顺序排序

排在集合{X,j,i} (X表示小于j的元素构成的某个集合)后面的集合为:
1. i< n 时,为{X,j,i,i+1}
2. i= n 时,为{X,j+1}, 特别地,当X为空集,且j+1=n时,所有子集已经确定。

#include<iostream>#include<vector>#include<algorithm>#include<iterator>using namespace std;void subsetHelper(vector<int> temp, int const& n, vector<vector<int>>& result){    if (n == 0){            sort(temp.begin(),temp.end());        result.push_back(temp);        return;    }    subsetHelper(temp, n - 1, result);//不选n    temp.push_back(n);//选n    subsetHelper(temp, n - 1, result);}/*这是一个求集合{1,2...n}的所有子集的函数,基于一棵完全二叉树*/void subset_v1(int const& n, vector<vector<int>>& result){    vector<int> temp;    subsetHelper(temp, n, result);}/*这是一个求集合{1,2...n}的所有子集的函数,基于bitpattern*/void subset_v2(int const &n, vector<vector<int>>& result){    vector<int> *temp;    vector<char> BitArray(n);//n 个字符(初始化为0)---> n bit     result.push_back(* new vector<int>());    int i, j;    while (true){        for (i = 0; i < n &&BitArray[i] == 1; BitArray[i] = 0, i++)//找到最右边的一个零            ;        if (i == n) break;//全部为1,所有情况都已经收录到result里了。        BitArray[i] = 1;        temp = new vector<int>();        for (j = 0; j < n; j++){            if (BitArray[j]) temp->push_back(j + 1);        }        result.push_back(*temp);    }}/*这是一个求集合{1,2...n}的所有子集的函数,按字典顺序排序。*/void subset_v3(int const &n, vector<vector<int>>& result){    int pos(0);    vector<int> *temp;    result.push_back(* new vector<int>());//初始添加一个没有大小的集合,也就是空集    temp = new vector<int>();    temp->push_back(1); pos++;    while (true)    {        result.push_back(*temp);//将上一轮循环得到的结果保存        if ((*temp)[pos - 1] < n){//最右边的元素不是最大值n时,继续往右走            temp = new vector<int>(*temp);            temp->push_back((*temp)[pos - 1] + 1); pos++;        }        else if (pos == 1){//{n}的情况,退出循环            break;        }        else{//{...i,n}的情况,这个集合的下一个集合因该是{...i+1}            pos--;            //新开辟大小为pos的vector            vector<int> *temp1 = new vector<int>(pos);            copy(temp->begin(), temp->begin() + pos, temp1->begin());            //上面一行不能写成 "copy(temp->begin(), temp->begin() + (pos - 1), temp1->begin());"。            //容器的定义是[begin, end),end不是容器中的一个元素。            temp = temp1;            (*temp)[pos - 1]++;//将第pos个元素加1。        }    }}int main(){    vector<vector<int>> result;    int n;    cout << "输入集合上限n:" << endl;    cin >> n;    subset_v1(n, result);       cout <<"subset_v1:------------------------基于完全二叉树深度搜索" << endl;    for (int i(0); i < result.size(); i++){        cout << "{";        copy(result[i].begin(), result[i].end(), ostream_iterator<int>(cout));        cout << "}";        cout << endl;    }    result.clear();    subset_v2(n, result);    cout << "subset_v2::------------------------基于集合的bitpattern表示" << endl;    for (int i(0); i < result.size(); i++){        cout << "{";        copy(result[i].begin(), result[i].end(), ostream_iterator<int>(cout));        cout << "}";        cout << endl;    }    result.clear();    subset_v3(n, result);    cout << "subset_v3::------------------------按字典排序之后" << endl;    for (int i(0); i < result.size(); i++){        cout << "{";        copy(result[i].begin(), result[i].end(), ostream_iterator<int>(cout));        cout << "}";        cout << endl;    }    system("pause");    return 0;}

按Gray码排序

什么是Gray 码? n bit 的Gray 码是一连串共有2n个元素的数列,每一个元素都有n bit ,
而且任何相邻的两个元素之间只有1bit 的值不同。例如, 3 个bit 的Gray 码:


000 001 011 010 110 111 101 100

是一组Gray 码,任何相邻两个元素都只有1 bit 值不同。但是, Gray 码却并不是惟一的。

把n=4 的一种Gray 码写出来:


0000 0001 0011 0010 0110 0111 0101 0100
1100 1101 1111 1110 1010 1011 1001 1000

发现从第1个数到第2个数是第1位变了,第2个数到第3个数是第2位变了,把所有位置写下来有:

1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1

可以归纳出:
第奇数个的数(0000算第0 个)永远是改变上一个值最右边一位而得来的,可能是0 变成1,也可能是1 变成0; 总之,变成了相反的值。
再看第偶数个的数(0011 ,0110,0101 , 1100, 1111 , 1010, 1001) ,它们是从改变上一个值的2,3 ,2,4,2,3,2 这几位而来;再仔细看看,它们的右边邻位(1,2,1,3,1,2,1 )值都是1,而且是从右边算过来的第一个1 。因此:
第偶数个的数(0000不计)的值是从改变上一个值中右边算来第一个1 的左边邻位而得来。

code:

/*这个函数用来按Gray码排序集合的所有子集, 所谓Gray是指相邻的两个码之间只有一位不同*/void subset_v4(int const &n, vector<vector<int>>& result){    vector<int> *temp;    //BitArray[0]--对应{xxxxxxx}中的最右边一位。    vector<char> BitArray(n);//n 个字符(初始化为0)---> n bit     result.push_back(*new vector<int>());//空集是任何集合的子集---对应{000...0}。    temp = new vector<int>();    temp->push_back(1);    result.push_back(*temp); //将1放入结果中---对应{000...01}    for (int i(1); i < n; i++){        BitArray[i] = 0;    }    BitArray[0] = 1;//初始BitArray ={1000...0}---对应{000...01}    int even = 1;//表示正在处理的是Gray码序列中的第偶数个数{类似数组,1对应0,2对应1...}。    int i;    while (true){        i = 0;        for (; i < n&&BitArray[i] == 0; i++);//找到右边第一个1。        if (i == n-1)break;        if (even)        {            BitArray[i + 1] = 1 - BitArray[i + 1];            even = 1 - even;        }        else{            BitArray[0] = 1 - BitArray[0];            even = 1 - even;        }        temp = new vector<int>();        for (int j(1); j <= n; j++){            if (BitArray[j - 1]) temp->push_back(j);        }        result.push_back(*temp);    }}
0 0
原创粉丝点击