您的位置:首页 > 其它

Subsets II [leetcode] 从获取子集的递归和循环方法说起,解决重复子集的问题

2014-09-26 21:39 489 查看
这一题和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]个元素
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐