加载中...
返回

难题本 | LeetCode90. 子集 II

题目链接:https://leetcode-cn.com/problems/subsets-ii/

题目

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

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

示例1

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

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

示例2

  • 输入: nums = [0]

  • 输出: [[],[0]]

分析

三个月之前WA过的题,在每日一题里碰见了,而我还是思考了很久 😢


原始数组中可能包含重复元素,则对于重复元素的考虑就尤为重要,一般来说,重复元素对于子集的贡献也有重复。

我们不妨考虑进行以下的模拟,对于一个 不包含重复元素 的集合 [1, 2, 3] ,如何求得它的所有子集呢?

首先,答案中包含一个空集。

首先取得第一个元素,将其与当前答案中的所有集合拼接,放入答案中;

考虑第二个元素,将其与当前答案中的所有集合拼接,放入答案中;

第三个元素同理。

在这个环节中,由于答案是在不断地增长的,我们只需要用一个变量保存添加元素之前的答案集合大小即可。

这种做法的正确性是 不会证明 理所当然的。

实际上,根据这种做法我们可以AC掉 LeetCode78. 子集

// LeetCode 78
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> tmp;
        vector<vector<int>> res;

        res.push_back(tmp);  // empty set

        for (auto n : nums)
        {
            int sze = res.size();
            for (int i = 0; i < sze; i++)
            {
                tmp = res[i];
                tmp.push_back(n);
                res.push_back(tmp);
            }
        }

        return res;
    }
};

那么问题在于,本题的原始数组是可以重复的。

对于重复数据,首先可以进行一波排序,使得我们比较方便地进行判重。

在上面的做法中,重复数据产生的答案会有相当一部分重复,我们考虑集合 [1, 2, 2]

可以看到,对于重复出现的 2 来说,它不需要再与 [][1] 进行合并。

那么,重复数据在哪里能够产生贡献呢?

答案就是 上一步新加入的子集

由此,形势就变得比较明朗了。对于非重复的数据,与目前拥有的所有子集进行合并;对于重复的数据,只与上一步加入的子集进行合并。

如何只获取上一步加入的子集呢?在上面给出的代码中,我们使用 sze 来记录答案集合中原本的大小。很巧的,上一步加入的子集在答案集合中的下标就从 sze 开始。

于是,使用一个变量 start 来记录本轮需要合并的子集起点,当当前的数是重复的, start 就被设置为 sze ,然后更新新的 sze ,否则 start 直接设置为 0

AC代码

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
        res.push_back(tmp);
        int start, sze;

        sort(nums.begin(), nums.end());

        for (int idx = 0; idx < nums.size(); idx++)
        {
            if (idx > 0 && nums[idx] == nums[idx - 1])
            {
                start = sze;
            }
            else
            {
                start = 0;
            }
            sze = res.size();
            for (int i = start; i < sze; i++)
            {
                tmp = res[i];
                tmp.push_back(nums[idx]);
                res.push_back(tmp);
            }
        }

        return res;
    }
};

小结

三个月前WA掉的题目在今天重新来到我的面前,经过一番思索之后能够独立地将其做出来了。人生点滴进步大抵皆见于此类细节之中。题解基本采用回溯的办法,在此没有花费精力予以研究。亦因此本文无所参考,便有,也是此三月中所见的某篇文章,回忆不清矣。

10 comments
Anonymous
Markdown is supported
@mpv945
mpv945commentedover 2 years ago

添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

@SGS4ever
SGS4evercommentedover 2 years ago

@mpv945
添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

我的博客没有使用图床,所以没办法帮到你~

@Celetherin
Celetherincommentedover 2 years ago

您好,我也是使用的stack主题,我在照着您的方法添加返回顶部按钮时,遇到了按钮虽然出现、也能够点击,但无法实现实际上的返回顶部功能的问题,我没有任何的代码知识,不知道您有没有解决方法?
另外,也是想提醒一下其他需要这篇教程的朋友,最新版的stack主题,添加返回按钮的组件应该在layouts/partials/sidebar/right.html, 在layouts/_default/single.html中添加代码会导致出现两个右边栏。

@jsjcjsjc
jsjcjsjccommentedover 2 years ago

请教一下博主,如何优雅的给stack主题添加广告哈?
我只想在左或者右侧边栏底部,或者每篇文章底部添加一个小小的广告,但是默认似乎的满屏广告哈~~
感谢

@SGS4ever
SGS4evercommentedover 2 years ago
@ClimbingMouse
ClimbingMousecommentedalmost 2 years ago

你好,按照你的方法设置页面载入动画,这个动画不会停止咋办啊

@46fafa
46fafacommentedalmost 2 years ago

博主你好,请问一下主页布局修改哪里的代码如何作用于整个网页,我发现修改后的布局只存在主页和前两篇文章,其他部分还是没修改的样子

@4kohakunushi
4kohakunushicommentedover 1 year ago

你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@SGS4ever
SGS4evercommentedover 1 year ago

首先我只能基于本文使用的Stack版本来尝试解答,因为没看过当前的Stack主题的代码~
我重新翻了下此前写的关于高亮的内容,理论上只要你的post页面的标题在menu配置中即可高亮。如果post页面是你站点的根路径,那应该可以参考我的文章里写的方法,修改下active的触发逻辑~

@4kohakunushi
你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@sansan-cc
sansan-cccommentedabout 1 month ago

感谢博主的建站帖子,有很大的帮助。

有朋自远方来,不亦说乎?