【Subsets】cpp
题目:
Given a set of distinct integers, nums, 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 nums = [1,2,3]
, a solution is:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
代码:
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { const int len = nums.size(); std::sort(nums.begin(), nums.end()); vector<vector<int> > ret = Solution::subsubsets(nums, 0, len-1); vector<int> none; ret.push_back(none); return ret; } static vector<vector<int> > subsubsets(vector<int>& nums, int begin, int end) { vector<vector<int> > ret; if ( begin>end ) return ret; for ( int i = begin; i <=end; ++i ) { // puts the curr value in vector<int> curr; curr.push_back(nums[i]); ret.push_back(curr); // get the subset vector<vector<int> > subset = Solution::subsubsets(nums, i+1, end); for ( int j = 0; j < subset.size(); ++j ) { //ret.push_back(subset[j]); subset[j].insert(subset[j].begin(), nums[i]); ret.push_back(subset[j]); } } return ret; } };
tips:
每层递归的任务是活的传入数组的全部子集
1. 留出第i个元素
2. 把i+1到end的元素送到下一层递归
3. 递归终止条件是begin>end
返回下一集全部子集后,算上第i个元素后的全部集合如下:
1)第i个元素单独算一个
2)第i个元素 + 子集中每个元素
这样递归到第0个元素,就得到了全部的非空子集;再根据题目要求补上空集。
============================================
完成之后觉得好像哪里不对。{1,2,3} 如果留下了1传入{2,3}时,这个过程中已经获得了{2,3}全部子集。
修改后的代码如下:
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { const int len = nums.size(); std::sort(nums.begin(), nums.end()); vector<vector<int> > ret = Solution::subsubsets(nums, 0, len-1); vector<int> none; ret.push_back(none); return ret; } static vector<vector<int> > subsubsets(vector<int>& nums, int begin, int end) { vector<vector<int> > ret; if ( begin>end ) return ret; // puts the curr value in vector<int> curr; curr.push_back(nums[begin]); ret.push_back(curr); // get the subset vector<vector<int> > subset = Solution::subsubsets(nums, begin+1, end); for ( int j = 0; j < subset.size(); ++j ) { ret.push_back(subset[j]); subset[j].insert(subset[j].begin(), nums[begin]); ret.push_back(subset[j]); } return ret; } };
tips:
这个版本整理了逻辑误区。
============================
又学习了一个深搜版本的代码,如下:
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { vector<vector<int> > ret; std::sort(nums.begin(), nums.end()); vector<int> tmp; Solution::dfs(ret, nums, 0, tmp); return ret; } static void dfs(vector<vector<int> >& ret, vector<int>& nums, int index, vector<int>& tmp) { if ( index==nums.size() ) { ret.push_back(tmp); return; } tmp.push_back(nums[index]); Solution::dfs(ret, nums, index+1, tmp); tmp.pop_back(); Solution::dfs(ret, nums, index+1, tmp); } };
Tips:
把集合元素想象成一个二叉树,如下
每层处理一个元素;往左分支代表加入这个元素;往右分支代表不加入这个元素;最终叶子节点就是全部子集。
这里需要维护一个层次变量index看是否到叶子了。
================================
再学习一个简单迭代的解法,代码如下:
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { vector<vector<int> > ret; std::sort(nums.begin(), nums.end()); vector<int> none; ret.push_back(none); for ( size_t i = 0; i < nums.size(); ++i ) { vector<vector<int> > subset = ret; for ( size_t j = 0; j < subset.size(); ++j ) { subset[j].push_back(nums[i]); ret.push_back(subset[j]); } } return ret; } };
tips:
1. 用subset记录上一轮结束后,所有子集;
2. 考虑subset中所有元素,加上nums[i]元素后多出来的集合,补充到ret中
这里注意,利用一个中间变量subset来保存ret上一轮的结果(或者保存上一组的指针)。
仔细想想,这套代码的思路与最开始递归代码的思路正好是逆向的:
1)递归的代码是留出来当前元素,去寻找其余所有元素组成的子集集合
2)迭代的代码是每次添加当前元素,并更新所有子集集合,直到添加入最后一个元素
================================================
第二次过这道题,首选的解法是简单迭代解法。这种方法的精髓是在于有一个none,这样每次都能把新元素单独列进来。
class Solution{ public: static vector<vector<int> > subsets(vector<int>& nums) { sort(nums.begin(), nums.end()); vector<vector<int> > ret; vector<int> none; ret.push_back(none); for ( int i=0; i<nums.size(); ++i ) { vector<vector<int> > tmp = ret; for ( int j=0; j<tmp.size(); ++j ) { tmp[j].push_back(nums[i]); ret.push_back(tmp[j]); } } return ret; } };
还用了dfs的解法。
class Solution { public: vector<vector<int> > subsets(vector<int>& nums) { sort(nums.begin(), nums.end()); vector<vector<int> > ret; vector<int> tmp; Solution::dfs(nums, ret, 0, tmp); return ret; } static void dfs( vector<int>& nums, vector<vector<int> >& ret, int index, vector<int>& tmp) { if ( index==nums.size() ) { ret.push_back(tmp); return; } tmp.push_back(nums[index]); Solution::dfs(nums, ret, index+1, tmp); tmp.pop_back(); Solution::dfs(nums, ret, index+1, tmp); } };
复习了dfs模板的写法。这个有个点需要注意,就是返回上一层时,一定要保证tmp是‘干净的’,因此,要有pop_back()这个动作。