首页 > 代码库 > Subsets II [leetcode] 从获取子集的递归和循环方法说起,解决重复子集的问题
Subsets II [leetcode] 从获取子集的递归和循环方法说起,解决重复子集的问题
这一题和Permutation II很像,一个是排列一个是组合。
我们理清思路,从最基本的获取子集的方法开始分析:
获取子集总的来说可以用循环或者递归做,同时还可以根据数字对应的binary code得到。
例如s = {x,y,z}可以生成的组合有:x,y,z,xy,yz,xz,xyz,0
第一种思路:
1. 维护一个集合Set(i),包含s[0...i]可生成的所有组合
s[0...i+1]可以生成的所有组合为:Set(i) + (Set(i)+s[i+1])
void permutation(vector<int> &s, vector<vector<int>> & res, int index) { permutation(s, res, index + 1); for (int i = res.size(); i >= 0; i—) { res.push_back(res[i] + s[index]); } }以上是一个递归的伪代码,我们可以将其改为循环的:
vector<vector<int>> permutation(vector<int> &s) { vector<vector<int>> res; for (int index = 0; index < s.size(); index++) { for (int i = res.size(); i >= 0; i—) { res.push_back(res[i] + s[index]); } } return res; }2.下面考虑如何去除相同的子集:
当输入为{x,y,y}时,对index = 0,res={0, x}; index = 1时res={0,x,y,xy}
当index=2时,问题来了。如果对0和x都加上y,会生成重复的子集y和xy。此时res={0,x,y,xy,y,xy,yy,xyy}
重复的原因是我们将index=1时处理过的集合{0,x}又处理了一遍。
所以我们加上preEnd标记上一个index处理过的集合的结尾位置(如{0,x})。
如果s[index]和s[index-1]一样,preEnd为处理s[index-1]之前res的大小;否则preEnd=0
代码如下:
vector<vector<int> > subsetsWithDup(vector<int> &s) { sort(s.begin(), s.end()); vector<vector<int>> res; res.push_back(vector<int>()); int preEnd = 0; for (int i = 0; i < s.size(); i++) { int resSize = res.size(); for (int j = preEnd; j < resSize; j++) { vector<int> curSet = res[j]; curSet.push_back(s[i]); res.push_back(curSet); } if (i + 1 < s.size() && s[i] == s[i + 1]) preEnd = resSize; else preEnd = 0; } return res; }
第二种思路:
1.再回看所有的子集{x,y,z,xy,yz,xz,xyz,0}。我们发现x没有出现在第二位,y没有出现在第三位...
1.1设{x,y,z}为Set1,则对Set1中的每个元素,向其添加可行的其他元素,如x可以添加y和z;y可以添加z;z已经到末尾了,不能添加其他元素了。所以我们得到Set2{xy,xz,yz}。
1.2 接着向Set2添加元素,得到Set3{xyz}。Set3不可能再扩展出其他子集了,所以循环结束。
{0, Set1, Set2, Set3}是最终结果。
递归的伪代码如下:
void permutation(vector<int> &s, int inPos, int outPos, vector<int> & out, vector<vector<int>> & res) { for (int index = inPos; index < s.size(); index++) { out[outPos] = s[index]; res.push_back(out); permutation(s, index + 1, outPos + 1, out, s); } }循环的时候,依次生成Set1,Set2,Set3。代码如下:
vector<vector<int> > subsets(vector<int> &s) { vector<vector<int>> res; vector<int> inPos; res.push_back(vector<int>()); inPos.push_back(-1); sort(s.begin(), s.end()); for (int i = 0; i < res.size(); i++) { for (int index = inPos[i] + 1; index < s.size(); index++) { vector<int> temp = res[i]; temp.push_back(s[index]); res.push_back(temp); inPos.push_back(index); } } return res; }下面考虑有重复元素的情况:
在生成每个Seti的时候,均有可能遇见重复元素,从而生成重复子集,所以在里层的for循环里要跳过相同元素。
这里只需要加上简单的一句话即可 while(index + 1 < s.size() && s[index] == s[index + 1]) index++;
代码如下:
vector<vector<int> > subsetsWithDup(vector<int> &s) { vector<vector<int>> res; vector<int> inPos; res.push_back(vector<int>()); inPos.push_back(-1); sort(s.begin(), s.end()); for (int i = 0; i < res.size(); i++) { for (int index = inPos[i] + 1; index < s.size(); index++) { vector<int> temp = res[i]; temp.push_back(s[index]); res.push_back(temp); inPos.push_back(index); while(index + 1 < s.size() && s[index] == s[index + 1]) index++; } } return res; }
当前子集包含/不包含 index
用递归来做的时候很简单,伪代码如下:
void permutation(vector<int> &s, int inPos, vector<int> & out, vector<vector<int>> & res) { if (inPos == s.size()) { res.push_back(out); return; } permutation(s, inPos + 1, out, res); permutation(s, inPos + 1, out + s[inPos], res); }非递归实现和第一种思路类似
第四种思路:
含有K个元素的集合有2^K个子集,每个数字的第i位代表是否含有第s[i]个元素
Subsets II [leetcode] 从获取子集的递归和循环方法说起,解决重复子集的问题