程序员面试金典: 9.9 递归和动态规划 9.4求某集合的所有子集

来源:互联网 发布:淘宝永久封店诈骗 编辑:程序博客网 时间:2024/05/17 01:10
#include <iostream>#include <stdio.h>#include <string>#include <vector>#include <algorithm>#include <sstream>using namespace std;/*问题:编写一个方法,返回某集合的所有子集。分析:什么集合,比如具体举个例子。一个有n个不同元素的集合,其子集为2^(n)     那么依次生成时间复杂度为O(2^n),这个时间复杂度要比O(n!)要大。 暴力破解应该是直接生成。 如果用递归:先生成一个初始元素,假设为abc,那么最后一个元素可以和倒数第二个元素进行交换 没明白题目的意思。 题目的实际意思是给定P={a1,a2,...an} 求出其所有子集。问题的关键是如何根据已经生成的元素来生成后续的集合。 比如当前子集元素为a,后续如何生成下一个子集中元素 注意该题并不是求字符串的全排列,全排列对于不同的顺序认为是不同的字符串,而集合中除了位置不同其余都相同的集合会认为是同一个。 P(0)={} P(1)={ {}, {a1} } P(2)={ {}, {a1}, {a2}, {a1,a2} } P(3)={ {}, {a1}, {a2}, {a1,a2} ,{a3} , {a1,a3}, {a2,a3}, {a1,a2,a3}  }P(2) + a3={ {a3} , {a1,a3}, {a2,a3}, {a1,a2,a3} } 观察P(3)-P(2)={                  {a3} , {a1,a3}, {a2,a3}, {a1,a2,a3} } 发现从P(2)构造P(3)只需要复制P(2),然加上a3, 即P(n) = P(n-1) + [ P(n-1) + an ]输入:3(集合中元素个数)a b c(集合中各个元素)输出:{} {a} {b} {c} {a,b} {a,c} {b,c} {a,b,c}书上解法2:因为n个元素,子集个数为2^n,那么对于,对于每个元素,存在选择和不选择,可以用1和0表示,等于实际上就是0~2^n - 1 的范围内,根据提取的二进制1的个数去获取集合中对应位置的元素来构成关键:1  题目的实际意思是给定P={a1,a2,...an} 求出其所有子集。问题的关键是如何根据已经生成的元素来生成后续的集合。 比如当前子集元素为a,后续如何生成下一个子集中元素 注意该题并不是求字符串的全排列,全排列对于不同的顺序认为是不同的字符串,而集合中除了位置不同其余都相同的集合会认为是同一个。 P(0)={} P(1)={ {}, {a1} } P(2)={ {}, {a1}, {a2}, {a1,a2} } P(3)={ {}, {a1}, {a2}, {a1,a2} ,{a3} , {a1,a3}, {a2,a3}, {a1,a2,a3}  }P(2) + a3={ {a3} , {a1,a3}, {a2,a3}, {a1,a2,a3} } 观察P(3)-P(2)={                  {a3} , {a1,a3}, {a2,a3}, {a1,a2,a3} } 发现从P(2)构造P(3)只需要复制P(2),然加上a3, 即P(n) = P(n-1) + [ P(n-1) + an ]2 因为n个元素,子集个数为2^n,那么对于,对于每个元素,存在选择和不选择,可以用1和0表示,等于实际上就是0~2^n - 1 的范围内,根据提取的二进制1的个数去获取集合中对应位置的元素来构成vector<string> getSubset(vector<string>& vecInputData , int n){vector<string> vecResult;int index = 0;for( int k = n ; k > 0 ; k >>= 1){//如果某个元素是被选中的if( (k & 1) == 1){string value = vecInputData.at(index);vecResult.push_back(value);}index++;}return vecResult;}3//将整个元素都拷贝到结果集中,用插入来做vecResult.insert(vecResult.end() , vecTemp.begin() , vecTemp.end() );*/string join(vector<string>& vecStr){if(vecStr.empty()){return "";}stringstream ss;ss << "{";int size = vecStr.size();for(int i = 0 ; i < size ; i++){if( i != 0){ss << "," << vecStr[i];}else{ss << vecStr[i] ;}}ss << "}";string sResult = ss.str();return sResult;}vector<string> getSubset(vector<string>& vecInputData , int n){vector<string> vecResult;int index = 0;for( int k = n ; k > 0 ; k >>= 1){//如果某个元素是被选中的if( (k & 1) == 1){string value = vecInputData.at(index);vecResult.push_back(value);}index++;}return vecResult;}void generateSubset_ByCombineNumber(vector<string>& vecInputData ,vector<string>& vecResult ){if(vecInputData.empty()){return ;}int size = vecInputData.size();int max = 1 << size;vector<string> subset;string sResult;for(int k = 0 ; k < max; k++){subset = getSubset(vecInputData , k);sResult = join(subset);vecResult.push_back(sResult);}}//n用于表示当前计算的子集的元素个数void generateSubset(vector<string>& vecInputData , vector<string>& vecResult , int n){if(vecInputData.empty()){return ;}//只有空集,返回if(-1 == n){vecResult.push_back("");}else{//先递归P(n-1),后处理generateSubset(vecInputData , vecResult , n - 1);// P(n) = P(n-1) + [ P(n-1) + an ]vector<string> vecTemp(vecResult);//对拷贝出来的每一个元素都加上 当前元素string val = vecInputData.at(n);for(vector<string>::iterator it = vecTemp.begin() ; it != vecTemp.end() ; it++){if(!(*it).empty()){*it += "," + val;}else{*it = val;}}//将整个元素都拷贝到结果集中,用插入来做vecResult.insert(vecResult.end() , vecTemp.begin() , vecTemp.end() );}}void process(){int n;vector<string> vecStr;vector<string> vecResult;string str;while(cin >> n){vecStr.clear();vecResult.clear();//考虑到n为0时,对应空集,这样处理//vecStr.push_back("");for(int i = 1 ; i <= n ; i++){cin >> str;vecStr.push_back(str);}//generateSubset_ByCombineNumber(vecStr , vecResult);generateSubset(vecStr , vecResult , vecStr.size() - 1 );//添加括号for(vector<string>::iterator it = vecResult.begin() ; it != vecResult.end() ; it++){*it = "{" + (*it) + "}";cout << (*it) << " ";}cout << endl;}}int main(int argc , char* argv[]){process();getchar();return 0;}

0 0
原创粉丝点击