标签:array backtracking leetcode
题目:
Given a collection of integers that might contain duplicates, S, return all possible subsets.
Note:
For example,
If S = [1,2,2]
, a solution is:
[ [2], [1], [1,2,2], [2,2], [1,2], [] ]
在上一道题 Subsets基础上,允许出现集合中包含重复数字,以集合{1,2,2}为例子,画出二叉树
Answer 1: DFS解法
思路:分析二叉树,我们知道处理第三个元素2时,因为前面已经出列一次2,所以第三层中,我们只在添加过2的结合{1,2}和{2}再添加2,而没有在集合{1},{}中添加2(画叉的那些分支),假设下面还有一个2,我们将只会在第四层包含两个2的集合{12,2}和{2,2}中添加2,其他都不添加。因此DFS, 如果当前处理的数字,在前面出现了k次(原集合S中),那么我们要处理的集合中必须包含k个该元素。否则会出现重复的集合。
Attention:
1. 首先要计算当前元素在原集合中出现了几次,计算k.这个首先可以先定位到第一个和S[ileaf]相同数字的位置,再做差得到重复次数。
<span style="font-size:14px;"> //fstname指向第一个和S[ileaf]相同数字的位置 int fstname = ileaf; do{ fstname--; }while(S[fstname] == S[ileaf]); fstname++; int sameNum = ileaf - fstname; //重复数字个数,不包含S[ileaf]本身</span>
<span style="font-size:14px;">//如果本轮处理不包含重复数字,或者当前处理的数字前面出现了k次,并且要处理的集合中包含k个该元素。加入S[ileaf] if(sameNum == 0 || (tmpres.size() >= sameNum && tmpres[tmpres.size() - sameNum] == S[ileaf])) { tmpres.push_back(S[ileaf]); subsetsWithDup_helper(S, ileaf + 1, tmpres, ret); tmpres.pop_back(); }</span>
AC Code:
class Solution { public: vector<vector<int> > subsetsWithDup(vector<int> &S) { vector<vector<int>> ret; if(S.size() == 0) return ret; vector<int> tmpres; sort(S.begin(), S.end()); subsetsWithDup_helper(S, 0, tmpres, ret); return ret; } private: void subsetsWithDup_helper(vector<int>& S, int ileaf, vector<int>& tmpres, vector<vector<int>>& ret) { if(ileaf == S.size()) { ret.push_back(tmpres); return; } //fstname指向第一个和S[ileaf]相同数字的位置 int fstname = ileaf; do{ fstname--; }while(S[fstname] == S[ileaf]); fstname++; int sameNum = ileaf - fstname; //重复数字个数,不包含S[ileaf]本身 //如果本轮处理不包含重复数字,或者当前处理的数字前面出现了k次,并且要处理的集合中包含k个该元素。加入S[ileaf] if(sameNum == 0 || (tmpres.size() >= sameNum && tmpres[tmpres.size() - sameNum] == S[ileaf])) { tmpres.push_back(S[ileaf]); subsetsWithDup_helper(S, ileaf + 1, tmpres, ret); tmpres.pop_back(); } subsetsWithDup_helper(S, ileaf + 1, tmpres, ret); return; } };
思路分析:
处理一个元素,分两种情况:
1)如果当前处理的元素没有出现过,则把前面得到的所有集合加上该元素,集合数翻倍。
2)如果当前处理的元素出现过,那么我们只把上一轮处理过的集合加上该元素(即上一轮添加了当前重复数字的集合)。比如,添加第二个2时,我们只把上一轮添加过2的集合{1,2}、{2}再添加一个2到结果中,其他集合不懂,{1}和{}是从上一层直接继承下来的,不做处理。由于我们是顺序添加集合到ret中的,所以处理过的集合总是排在ret的后面位置。
Attention:
1. 需要维护两个变量,上一次处理的数字,即将要进行操作的子集数量。
<span style="font-size:14px;">//如果本次处理数字和上次不重复,更新这两个变量 if(S[i] != last) { last = S[i]; opResNum = ret.size(); }</span>2.如果本次处理元素重复,将不更新opResNum,则 ret.size() - opResNum不等于0,我们只处理后面的res.size() - opResNum个集合。
<span style="font-size:14px;">//如果出现重复数字,本次处理的集合数和上次一样,即只处理上次处理过的集合。resSize - opResNum,如果opResNum不等于res.size(),则只处理上次处理过的集合。由于迭代是顺序添加的,所以上次处理过的集合一定排在ret的末尾res.size() - opResNum个。 int retSize = ret.size(); for(int j = retSize - 1; j >= retSize - opResNum; j--) { ret.push_back(ret[j]); ret.back().push_back(S[i]); }</span>
class Solution { public: vector<vector<int> > subsetsWithDup(vector<int> &S) { int len = S.size(); sort(S.begin(), S.end()); vector<vector<int>> ret(1); //上一个处理的数字,即将要进行操作的子集数量 int last = S[0], opResNum = 1; for(int i = 0; i < S.size(); i++) { //如果本次处理数字和上次不重复,更新这两个变量 if(S[i] != last) { last = S[i]; opResNum = ret.size(); } //如果出现重复数字,本次处理的集合数和上次一样,即只处理上次处理过的集合。resSize - opResNum,如果opResNum不等于res.size(),则只处理上次处理过的集合。由于迭代是顺序添加的,所以上次处理过的集合一定排在ret的末尾res.size() - opResNum个。 int retSize = ret.size(); for(int j = retSize - 1; j >= retSize - opResNum; j--) { ret.push_back(ret[j]); ret.back().push_back(S[i]); } } return ret; } };
标签:array backtracking leetcode
原文地址:http://blog.csdn.net/cinderella_niu/article/details/42293601