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


    78. 子集

    题目描述:

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

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

    解答:

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

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

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

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

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

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

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

    代码实现:

    1. class Solution {
    2. public:
    3. vectorint>> result;
    4. vector<int> path;
    5. void backTrace(vector<int>& nums, int start){
    6. //超过边界 终止
    7. if (start >= nums.size()){
    8. return;
    9. }
    10. for (int i = start; i < nums.size(); i++){
    11. path.push_back(nums[i]);
    12. //每次都需要放入结果集
    13. result.push_back(path);
    14. backTrace(nums, i + 1);
    15. path.pop_back();
    16. }
    17. }
    18. vectorint>> subsets(vector<int>& nums) {
    19. //放入空集
    20. result.push_back(path);
    21. backTrace(nums, 0);
    22. return result;
    23. }
    24. };

    90. 子集 II

    题目描述:

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

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

    解答:

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

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

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

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

    代码实现:

    1. class Solution {
    2. public:
    3. vector<int>path;
    4. vectorint>>result;
    5. void backTrace(vector<int>& nums, int start){
    6. if (start >= nums.size())
    7. return;
    8. for (int i = start; i < nums.size(); i++){
    9. if (i > start && nums[i] == nums[i - 1])
    10. continue;
    11. path.push_back(nums[i]);
    12. result.push_back(path);
    13. backTrace(nums, i + 1);
    14. path.pop_back();
    15. }
    16. }
    17. vectorint>> subsetsWithDup(vector<int>& nums) {
    18. sort(nums.begin(), nums.end());
    19. result.push_back(path);
    20. backTrace(nums, 0);
    21. return result;
    22. }
    23. };

    491. 递增子序列

    题目描述:

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

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

    解答:

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

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

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

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

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

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

    如[4, 7, 6, 7]:

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

    (1)参数及返回值:

    1. vector<int>path;
    2. vectorint>>result;
    3. void backTrace(vector<int>& nums, int start){}

     (2)终止条件:

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

    (3)内部处理逻辑:

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

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

    代码实现:

    1. class Solution {
    2. public:
    3. vector<int>path;
    4. vectorint>>result;
    5. void backTrace(vector<int>& nums, int start){
    6. if (start >= nums.size())
    7. return;
    8. unordered_set<int> uset; // 使用set对本层元素进行去重
    9. for (int i = start; i < nums.size(); i++){
    10. //去重
    11. if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end())
    12. continue;
    13. path.push_back(nums[i]);
    14. uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
    15. if (path.size() >= 2)
    16. result.push_back(path);
    17. backTrace(nums, i + 1);
    18. path.pop_back();
    19. }
    20. }
    21. vectorint>> findSubsequences(vector<int>& nums) {
    22. backTrace(nums, 0);
    23. return result;
    24. }
    25. };

  • 相关阅读:
    特斯拉质量总监离职,竟是被马斯克挚友挖走?
    JVM—Class类文件结构详解
    ModuleNotFoundError: No module named ‘office’ - Python自动化办公,常见问题【01】
    Effective C++条款24:若所有参数皆需类型转换,请为此采用non-member函数
    寻找AI时代的关键拼图,从美国橡树岭国家实验室读懂AI存力信标
    Android开发基础——ListView
    Git使用教程:入门到精通
    后端配置拦截器的一个问题
    linux 内存检测工具 kfence 详解(二)
    flask bootstrap页面json格式化
  • 原文地址:https://blog.csdn.net/weixin_41997940/article/details/126459249