每日刷题|回溯法解决子集问题

简介: 每日刷题|回溯法解决子集问题

回溯法的理解

本文参考了一位大佬的题解,详细的介绍了回溯法:链接

上一篇刷题文: 回溯法经典问题之组合

       记住住一句话:for循环横向遍历,递归纵向遍历,回溯不断调整结果集。 这句话将从始至终贯穿我们对于以上问题的回溯解决办法。

💮 一、子集

题目链接:78. 子集

题目描述:

       给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]

输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]


示例 2:

输入:nums = [0]

输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

本题思路:

      采用经典的“回溯三部曲”:

       1、定义两个全局变量,一个用来存放符合条件单一结果(path),一个用来存放符合条件结果的集合(result)。注意要记得用一个变量来记录遍历的位置

       2、回溯的主体,回溯终止条件。path保存一组数据,每次遍历到叶子节点,再插入到result中,并且回溯到上一个节点。

       3、单层搜索的过程。for循环用来横向遍历,递归的过程是纵向遍历。


特别注意: 本题虽然同之前的组合问题差不多,但是还是需要做一定的变化的,依据题意,我们都知道任何一个子集都是包涵空集的,并且子集中一个元素也算子集。

        于是根据题意写出以下题解:

class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtrack(vector<int>& nums,int index)
{
    result.push_back(path);   
    if(index==nums.size())
    {
        return;
    }
    for(int i=index;i<nums.size();i++)
    {
        path.push_back(nums[i]);
        backtrack(nums,i+1);
        path.pop_back();
    }
}
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        path.clear();
        result.clear();
        sort(nums.begin(),nums.end());
        backtrack(nums,0);
        return result;
    }
};

🌺二、子集II

题目链接:90. 子集 II

题目描述:

       给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]

输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]


示例 2:

输入:nums = [0]

输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10

 本题思路:

       同样采用经典的“回溯三部曲”,但是由于此题中的元素是可以重复的,那这样必然会出现重复的子集,因此我们需要进行剪枝操作。此时可以参考 回溯法经典问题之组合中最后一题。我们也建立一个used,用于标记是否使用过使用过元素。

    特别注意: 本题虽然同之前的题差不多,但是还是需要做一定的变化的。比如插入的操作。

       一图了解题解 ~(以{1,2,2}为例)

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



感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!  

目录
打赏
0
0
0
0
6
分享
相关文章
Flink--7、窗口(窗口的概念、分类、API、分配器、窗口函数)、触发器、移除器
Flink--7、窗口(窗口的概念、分类、API、分配器、窗口函数)、触发器、移除器
【LeetCode力扣】234 快慢指针 | 反转链表 | 还原链表
【LeetCode力扣】234 快慢指针 | 反转链表 | 还原链表
137 0
【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)
贪心算法是一种非常常见的算法,它的简单和高效性使其在实际应用中被广泛使用。 贪心算法的核心思想是在每一步都采取当前状态下最优的选择,而不考虑未来可能产生的影响。虽然贪心算法不能保证总是得到最优解,但在很多情况下,它可以获得很好的结果。 本篇文章将介绍贪心算法的基本概念和一些经典应用,以及如何通过贪心算法来解决一些实际问题。希望通过本文的阅读,读者可以对贪心算法有更加深刻的理解,并能够在实际问题中应用贪心算法来得到更好的解决方案。 让我们暴打贪心算法吧!
5327 0
Linux远程访问Windows实现步骤
在Windows上启用远程桌面连接并获取IP地址后,Linux用户需安装SSH客户端( Debian系:`sudo apt-get update; sudo apt-get install openssh-client`,RPM系:`sudo yum install openssh-clients`)。然后使用命令`ssh 用户名@Windows_IP地址`连接,其中`用户名`和`Windows_IP地址`按实际情况填写。
163 4
云服务器关机时可以重置密码吗?
【10月更文挑战第6天】云服务器关机时可以重置密码吗?
109 0
更改 TensorFlow Hub 模型的缓存位置
更改 TensorFlow Hub 模型的缓存位置
102 0
LeetCode 题目 88:双指针\直接\递归\插入排序\归并排序 实现合并两个有序数组
LeetCode 题目 88:双指针\直接\递归\插入排序\归并排序 实现合并两个有序数组
一篇文章带你了解Spring声明式事务的底层实现原理。
1. Mybatis是如何整合进Spring中的 - Spring如何知道哪些接口是Mapper接口的? - Mapper接口是如何变成Spring Bean的? 2. Spring在哪里声明的SqlSession的实现逻辑? 3. Spring中声明式事务的实现方式是怎样的? 4. Spring中如何处理嵌套事务的? 5. Spring中事务的传播方式是如何实现的?
1048 0
一篇文章带你了解Spring声明式事务的底层实现原理。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等