力扣第90题 子集|| 去重问题 c++ 注释版
题目
中等
相关标签
给你一个整数数组 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
思路和解题方法
- 首先,定义了私有成员变量
result
和path
,分别用于存储最终结果和当前路径。result
是一个二维向量,每个元素是一个一维向量,表示一个子集。path
是一个一维向量,表示当前路径。- 接下来是私有函数
backtracking
,它是实现回溯算法的核心部分。回溯算法是一种通过穷举所有可能的搜索方式,然后根据条件进行剪枝的算法。在这里,回溯算法用于生成给定数组的所有子集,并避免生成重复的子集。- 在
backtracking
函数中,首先将当前路径path
加入到结果集result
中,即将当前路径作为一个子集加入结果集。- 接着,使用循环从
startIndex
开始遍历数组nums
的元素。对于每个元素nums[i]
,判断其是否与前一个元素相同且不是起始位置。如果是,则跳过该元素,以避免生成重复的子集。这个判断条件if (i > startIndex && nums[i] == nums[i-1])
起到了去重的作用。- 如果当前元素需要包含在子集中,则将其加入到当前路径
path
中。然后,递归调用backtracking
函数,以处理下一个元素。递归调用时,将startIndex设置为i + 1,表示从当前元素的下一个元素开始进行处理。- 当递归调用完成后,进行回溯操作。即将路径
path
中的当前元素移除,尝试其他选择,继续搜索其他子集。- 最后,公共函数
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<vector<int>> result; // 存储最终结果的二维向量
vector<int> path; // 存储当前路径的一维向量
void backtracking(vector<int>& 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<vector<int>> subsetsWithDup(vector<int>& 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
关键字跳过重复元素,以避免生成重复的子集。
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)