【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()这个动作。

原文地址:https://www.cnblogs.com/xbf9xbf/p/4516802.html