力扣第90题 子集|| 去重问题 c++ 注释版

题目

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

思路和解题方法

  1. 首先,定义了私有成员变量resultpath,分别用于存储最终结果和当前路径。result是一个二维向量,每个元素是一个一维向量,表示一个子集。path是一个一维向量,表示当前路径。
  2. 接下来是私有函数backtracking,它是实现回溯算法的核心部分。回溯算法是一种通过穷举所有可能的搜索方式,然后根据条件进行剪枝的算法。在这里,回溯算法用于生成给定数组的所有子集,并避免生成重复的子集。
  3. backtracking函数中,首先将当前路径path加入到结果集result中,即将当前路径作为一个子集加入结果集。
  4. 接着,使用循环从startIndex开始遍历数组nums的元素。对于每个元素nums[i],判断其是否与前一个元素相同且不是起始位置。如果是,则跳过该元素,以避免生成重复的子集。这个判断条件if (i > startIndex && nums[i] == nums[i-1])起到了去重的作用。
  5. 如果当前元素需要包含在子集中,则将其加入到当前路径path中。然后,递归调用backtracking函数,以处理下一个元素。递归调用时,将startIndex设置为i + 1,表示从当前元素的下一个元素开始进行处理。
  6. 当递归调用完成后,进行回溯操作。即将路径path中的当前元素移除,尝试其他选择,继续搜索其他子集。
  7. 最后,公共函数subsetsWithDup是对外的接口函数。它首先清空结果集result和路径path,以便重新计算子集。然后,对输入数组nums进行排序,以便处理重复元素的情况。排序后,调用backtracking函数,从索引0开始进行回溯搜索。最终,返回结果集result

复杂度

        时间复杂度:

                O(2^N * N)

        该代码的时间复杂度为 O(2^N * N),其中 N 是数组 nums 的长度。

在回溯函数 backtracking 中,我们遍历了所有可能的子集组合。每个元素都有两种状态:选中和不选中,因此总共有 2^N 种可能的子集。在每次添加到结果集中时,需要复制一份当前路径 path,并将其加入到结果集中,这个操作的时间复杂度是 O(N)。

因此,回溯函数的时间复杂度为 O(2^N * N)。

        空间复杂度

                O(N)

对于空间复杂度,主要耗用的是递归调用栈的空间。在最坏情况下,递归的深度可以达到 N,所以空间复杂度是 O(N)。另外,还需要额外的空间来存储结果集和当前路径,因此额外的空间复杂度是 O(N)。

c++ 代码

class Solution {
private:
    vector> result; // 存储最终结果的二维向量
    vector path; // 存储当前路径的一维向量

    void backtracking(vector& nums, int startIndex) {
        result.push_back(path); // 将当前路径加入结果集中

        for (int i = startIndex; i < nums.size(); i++) {
            if (i > startIndex && nums[i] == nums[i-1]) {
                continue; // 如果遇到重复的元素,则跳过,避免生成重复的子集
            }//重要 ,相邻两个去重

            path.push_back(nums[i]); // 将当前元素加入路径中
            backtracking(nums, i + 1); // 递归调用,处理下一个元素
            path.pop_back(); // 回溯,将当前元素移出路径,尝试其他选择
        }
    }

public:
    vector> subsetsWithDup(vector& nums) {
        result.clear(); // 清空结果集
        path.clear(); // 清空路径
        sort(nums.begin(), nums.end()); // 对数组进行排序,以便处理重复元素
        backtracking(nums, 0); // 调用回溯函数,从索引0开始搜索
        return result; // 返回最终的结果集
    }
};

分析

  if (i > startIndex && nums[i] == nums[i-1]) {
                continue; // 如果遇到重复的元素,则跳过,避免生成重复的子集
            }//重要 ,相邻两个去重

        代码是为了避免生成重复的子集而进行的判断和跳过操作。

        首先,根据题目要求,我们需要找到给定数组中所有不重复的子集。如果数组中存在重复的元素,并且我们在生成子集时不进行特殊处理,就有可能生成重复的子集。

在这段代码中,通过比较当前元素 nums[i] 和前一个元素 nums[i-1] 是否相等,来判断是否遇到重复的元素。为了确保判断的准确性和避免数组越界,还需要额外的条件 i > startIndex 来保证当前元素 nums[i] 不是起始位置的元素。

        如果发现相邻两个元素相等,就说明当前元素 nums[i] 与前一个元素 nums[i-1] 是重复的。为了避免生成重复的子集,我们直接使用 continue 关键字跳过当前循环,不将当前元素 nums[i] 加入到当前路径 path 中。

这样,当数组中有连续重复的元素时,我们只会选择其中的一个,从而避免了生成重复的子集。

        需要注意的是,在进行这一判断和跳过操作之前,必须确保数组 nums 是已经按照非降序排序的,这样才能保证重复的元素是相邻的。否则,不相邻的重复元素也可能被认为是重复的,导致错误结果。

        综上所述,这段代码通过比较当前元素与前一个元素是否相等,来判断是否遇到重复的元素,并通过 continue 关键字跳过重复元素,以避免生成重复的子集。

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

你可能感兴趣的:(leetcode,回溯,数据结构,leetcode,c++,算法,回溯)