[Subsets I] Given a set of distinct integers, S, return all possible subsets. Note: Elements in a subset must be in non-descending order. The solution set must not contain duplicate subsets. For example, If S = [1,2,3], a solution is: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]. [Subset II]: Given a collection of integers that might contain duplicates, S, return all possible subsets.
// there are two solutions. One is the normal DFS using the ordinary logic. The other is different.
// Its advantage is that, its logic can be extented to the "Subsets II" problem with only little updates.
// Solution 1: normal dfs, easy to think, but cannot handle duplications.
// idea: contain or not contain a certain item in the set
void DFS(vector<vector<int>> &res, vector<int> one_subset, vector<int> &S, int i, int N) {
if(i == N) {
res.push_back(one_subset);
return;
}
DFS(res, one_subset, S, i+1, N); // do not contain S[i]
one_subset.push_back(S[i]);
DFS(res, one_subset, S, i+1, N); // contain S[i]
one_subset.pop_back();
}
vector<vector<int>> subsets(vector<int> &S) {
vector<vector<int>> res;
if(S.empty()) return res;
sort(S.begin(), S.end()); // gist
vector<int> one_subset;
DFS(res, one_subset, S, 0, S.size());
return res;
}
// ---------------------------------------------------------------------------------------
// Solution 2: best version.
// sort, then consider one by one. The idea is quite different from the above one.
void DFS2(vector<vector<int>> &res, vector<int> onesubset, vector<int> &S, int pos) {
for(int i=pos; i<S.size(); i++) {
if(i > pos && S[i] == S[i-1]) continue; // gist5, handle duplicate elements in set!
onesubset.push_back(S[i]);
res.push_back(onesubset); // gist3, everytime we update onesubset, we add it to final res
DFS2(res, onesubset, S, i+1);
onesubset.pop_back();
}
}
vector<vector<int>> subsets(vector<int> &S) {
vector<vector<int>> res;
if(S.empty()) return res;
sort(S.begin(), S.end()); // gist1, sort
vector<int> one_subset;
res.push_back(one_subset); // gist2, push an empty item beforehand.
DFS2(res, one_subset, S, 0);
return res;
}
// ---------------------------------------------------------------------------------------
// solution 3, push to res outside of loop. little hard to think.
void DFS3(vector<vector<int>>& res, vector<int> onesubset, vector<int>& S, int pos) {
res.push_back(onesubset); // push before loop
for(int i=pos; i<S.size(); i++) {
if(i > pos && S[i] == S[i-1]) continue; // handle duplications
onesubset.push_back(S[i]);
DFS3(res, onesubset, S, i+1);
onesubset.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& S) {
vector<vector<int>> res;
if(S.empty()) return res;
sort(S.begin(), S.end());
vector<int> one_subset;
DFS3(res, one_subset, S, 0);
return res;
}
// Example: expand the recursive function DFS2 on set S = [1,2,3].
/*
S = [1,2,3]
dfs2(res, [], S, 0) {
i=0
onesubset = [1] <------
res = [[1]]
dfs2(res, [1], S, 1) {
i=1
onesubset=[1,2] <------
dfs2(res, [1,2], S, 2) {
i=2
onesubset=[1,2,3] <------
dfs2(res, [1,2,3], S, 3)
onesubset=[1,2]
}
onesubset=[1]
i=2
onesubset=[1,3] <------
dfs2(res, [1,3], S, 3)
onesubset=[1]
}
onesubset = []
i=1
onesubset = [2] <------
res = [[...], [...], [2]]
dfs2(res, [2], S, 2) {
i=2
onesubset=[2,3] <------
dfs2(res, [2,3], S, 3)
}
onesubset = []
i=2
onesubset=[3] <------
res = [[...], [...], ..., [3]]
dfs2(res, [3], S, 3)
onesubset=[]
}
*/