程序员面试金典: 9.9 递归和动态规划 9.4求某集合的所有子集
2017-01-08 18:17
369 查看
#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; }
相关文章推荐
- 9.9递归和动态规划(四)——返回某集合的所有子集
- 9.9递归和动态规划(四)——返回某集合的全部子集
- 程序员面试金典(动态规划):返回某集合的所有子集(java解法)
- 程序员面试金典: 9.9 递归和动态规划 9.5求字符串的全排列
- 递归求集合的所有子集的程序
- 9.9递归和动态规划(六)——打印n对括号的所有有效组合(即左右括号正确配对)
- 程序员面试金典: 9.9 递归和动态规划 9.2机器人走路的方式
- 求一个集合的所有子集 输出一个数所有平方和的情况 背包问题的递归解决
- 程序员面试金典: 9.9 递归和动态规划 9.2机器人走路的方式_避免禁区
- C语言递归求解集合的所有子集
- 程序员面试金典: 9.9 递归和动态规划 9.6打印n对括号的全部有效组合
- 程序员面试金典: 9.9 递归和动态规划 9.3魔术索引
- CCI 9.4 集合的所有子集
- 程序员面试金典: 9.9 递归和动态规划 9.8求n分可以由25分,10分,5分,1分的硬币的表示方法
- 程序员面试金典: 9.9 递归和动态规划 9.11求布尔表达式的表达个数
- Java递归求某个集合的所有子集组成的集合,即幂集
- 使用递归求出一个集合的所有子集
- 程序员面试金典: 9.9 递归和动态规划 9.9八皇后问题
- 程序员面试金典: 9.9 递归和动态规划 9.7颜色填充
- 程序员面试金典: 9.9 递归和动态规划 9.10求堆出箱子的最大高度