78. 子集、90. 子集 II、491. 递增子序列

78. 子集

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集

解答:

子集问题其实和组合问题很相似,不同在于子集问题需要在每次取数后都存入结果集

也就是组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点! 

其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。

那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

相应的,需要考虑次序时那就需要从0开始了(比如排列问题)。 

此外子集问题终止条件有所不同,子集问题在搜索到超过数组边界时即为终止

需要额外注意的是空集也需要加入到结果集中去。

代码实现:

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backTrace(vector<int>& nums, int start){
        //超过边界 终止
        if (start >= nums.size()){
            return;
        }
        for (int i = start; i < nums.size(); i++){
            path.push_back(nums[i]);
            //每次都需要放入结果集
            result.push_back(path);
            backTrace(nums, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        //放入空集
        result.push_back(path);
        backTrace(nums, 0);
        return result;
    }
};

90. 子集 II

题目描述:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

解答:

这道题和上一题的不同在于nums数组中包含重复元素,这样一来和 40. 组合总和 II 可以说是十分相似了(见39. 组合总和、40. 组合总和 II_清榎的博客-CSDN博客

那其实只需要在上一题的基础上先排序,再搜索整棵树,进行去重也就可以了。

两种方法,一种使用 used数组,另一种使用i和start的大小

此处使用第二种办法,只有在树中同一层的元素才会出现i>start的情况,所以i > start && nums[i] == nums[i - 1]时进行去重。

代码实现:

class Solution {
public:
    vector<int>path;
    vector<vector<int>>result;
    void backTrace(vector<int>& nums, int start){
        if (start >= nums.size())
            return;
        for (int i = start; i < nums.size(); i++){
            if (i > start && nums[i] == nums[i - 1])
                continue;
            path.push_back(nums[i]);
            result.push_back(path);
            backTrace(nums, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        result.push_back(path);
        backTrace(nums, 0);
        return result;
    }
};

491. 递增子序列

题目描述:

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

解答:

这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。

这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的90.子集。

就是因为太像了,更要注意差别所在,要不就掉坑里了!

在90.子集中我们是通过排序,再加一个标记数组或者i>start来达到去重的目的。

而本题求自增子序列,是不能对原数组经行排序的,排完序的数组都是自增子序列了。

所以不能使用之前的去重逻辑!

如[4, 7, 6, 7]:

491. 递增子序列1因为本题稍有复杂,考虑回溯三要素:

(1)参数及返回值:

    vector<int>path;
    vector<vector<int>>result;
    void backTrace(vector<int>& nums, int start){}

 (2)终止条件:

本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和​​​​​​​本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和90.子集一样。也可以不加终止条件,start每次都会加1,并不会无限递归。

(3)内部处理逻辑:

先进行去重,如果下个元素大于path中最后一个元素或者同一层中已经使用过就忽略。有无使用过采用哈希表进行标记。

内部处理时哈希表也需要相应进行修改。

代码实现:

class Solution {
public:
    vector<int>path;
    vector<vector<int>>result;
    void backTrace(vector<int>& nums, int start){
        if (start >= nums.size())
            return;
        unordered_set<int> uset; // 使用set对本层元素进行去重
        for (int i = start; i < nums.size(); i++){
            //去重
            if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end())
                continue;
            path.push_back(nums[i]);
            uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
            if (path.size() >= 2)
                result.push_back(path);
            backTrace(nums, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backTrace(nums, 0);
        return result;
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值