刷题记录 回溯算法-13:90. 子集 II

news/2025/1/16 8:45:15/

题目: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.子集问题

子集问题是边访问边收集结果,

不仅在回溯树叶节点收集结果,

因此return和收集结果命令不在一起

2.搜索集

单独搜索集,

无重复元素和重复选取

终止条件是最简单的访问到终点

二、代码实现

同层重复剪枝有多种实现方式:

1.基于同层索引去重

简单判断:

class Solution:def backtracking(self, nums, start_idx, path, ans):ans.append(path[:])if start_idx == len(nums):returnfor i in range(start_idx, len(nums)):if i > start_idx and nums[i] == nums[i - 1]:continuepath.append(nums[i])self.backtracking(nums, i + 1, path, ans)path.pop()def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:ans = []nums.sort()self.backtracking(nums, 0, [], ans)return ans

same_level数组去重:

代码随想录中把层与层之间传递的数组称为used,

预定义为True,访问后改为False,回溯时改回来

我觉得这样有点不好理解,

所以我把这个数组名称改为same_level,检视是否为同层,

这样True和False的机制就好理解多了

class Solution:def backtracking(self, nums, start_idx, same_level, path, ans):ans.append(path[:])if start_idx == len(nums):returnfor i in range(start_idx, len(nums)):if i > 0 and nums[i] == nums[i - 1] and same_level[i - 1]:continuepath.append(nums[i])same_level[i] = Falseself.backtracking(nums, i + 1, same_level, path, ans)same_level[i] = Truepath.pop()def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:ans = []nums.sort()same_level = [True] * len(nums)self.backtracking(nums, 0, same_level, [], ans)return ans

 2.基于数值哈希去重

used数值集合(set):

class Solution:def backtracking(self, nums, start_idx, path, ans):ans.append(path[:])if start_idx == len(nums):returnused = set()for i in range(start_idx, len(nums)):ch = nums[i]if ch in used:continuepath.append(ch)used.add(ch)self.backtracking(nums, i + 1, path, ans)path.pop()def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:ans = []nums.sort()self.backtracking(nums, 0, [], ans)return ans

used数值数组(list):

代码随想录中的

class Solution:def backtracking(self, nums, start_idx, path, ans):ans.append(path[:])if start_idx == len(nums):returnused = [False] * 21for i in range(start_idx, len(nums)):ch = nums[i]if used[ch + 10]:continuepath.append(ch)used[ch + 10] = Trueself.backtracking(nums, i + 1, path, ans)path.pop()def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:ans = []nums.sort()self.backtracking(nums, 0, [], ans)return ans

三、从两类代码写法中看排序的作用

1.两类写法去重机制上的不同

从代码实现部分能看到同层去重有基于同层索引和基于数值哈希两种去重方式

基于索引的前提条件时相同的数值需要挨在一起,

基于数值哈希仿佛没有前提条件,凭借哈希表可以常数时间找到同层重复

也就是说数值哈希仿佛不需要排序了,

是这样吗?

做个测试

2.测试:去排序的数值哈希实现

测试代码:

class Solution:def backtracking(self, nums, start_idx, path, ans):ans.append(path[:])if start_idx == len(nums):returnused = set()for i in range(start_idx, len(nums)):ch = nums[i]if ch in used:continuepath.append(ch)used.add(ch)self.backtracking(nums, i + 1, path, ans)path.pop()def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:ans = []# nums.sort()self.backtracking(nums, 0, [], ans)return ans

测试用例:

nums = [4,4,4,1,4]

测试结果:

输出

[[],[4],[4,4],[4,4,4],[4,4,4,1],[4,4,4,1,4],[4,4,4,4],[4,4,1],[4,4,1,4],[4,1],[4,1,4],[1],[1,4]]

预期结果

[[],[1],[1,4],[1,4,4],[1,4,4,4],[1,4,4,4,4],[4],[4,4],[4,4,4],[4,4,4,4]]

3.排序的两重作用

先说结论:排序有把相同数字紧贴防止异层重复访问两个作用

解释探究过程:

测试结果表明,

去排序后数值哈希的去重作用消失

为什么去掉了排序数值哈希就失效了呢?

事实上并没有完全失效,如果把数值哈希命令去掉:

输出

[[],[4],[4,4],[4,4,4],[4,4,4,1],[4,4,4,1,4],[4,4,4,4],[4,4,1],[4,4,1,4],[4,4,4],[4,4],[4,4,1],[4,4,1,4],[4,4,4],[4,1],[4,1,4],[4,4],[4],[4,4],[4,4,1],[4,4,1,4],[4,4,4],[4,1],[4,1,4],[4,4],[4],[4,1],[4,1,4],[4,4],[1],[1,4],[4]]

说明去排序的数值哈希还是有一点作用的,但没有达到要求 

观察两个输出会发现,

数值哈希的确会起到同层去重的作用,

只是由于没有排序,重复虽然不会在同层出现,

但可能会在层与层之间出现重复

也就是说,排序的作用除了让相同数字紧贴方便基于索引的方式去重外

还有把所有可能发生的重复集中在一层中,

防止重复出现在回溯树的多个层中


http://www.ppmy.cn/news/1563554.html

相关文章

钉钉消息推送()

记录一下java实现消息推送 1. 首先添加依赖 <dependencies><dependency><groupId>com.aliyun</groupId><artifactId>alibaba-dingtalk-service-sdk</artifactId><version>2.0.0</version></dependency><dependency&…

Python Chardet 库详解:字符编码检测的利器

Python Chardet 库详解&#xff1a;字符编码检测的利器 在处理文本数据时&#xff0c;字符编码问题是一个常见的挑战。如果编码不正确&#xff0c;可能会导致乱码问题。而 Chardet 是 Python 中非常实用的一个库&#xff0c;可以帮助我们快速检测文件或字符串的编码格式。 1.…

【微服务】面试 2、服务雪崩

服务雪崩概念 主要内容&#xff1a;在微服务项目中&#xff0c;微服务间存在远程调用。若某一服务&#xff08;如服务 d&#xff09;出现故障&#xff0c;调用它的服务&#xff08;如服务 a&#xff09;会失败。若调用方持续向故障服务发起请求&#xff0c;由于服务连接数有限且…

2025Paypal取消到期自动续费(循环付款)教程

今天订阅了PixivFanbox&#xff0c;怎么取消自动订阅&#xff0c;防止大家被坑&#xff0c;就开个帖子&#xff0c;即便在Fanbox取消订阅&#xff0c;发现Paypal的自动订阅还在&#xff0c;防止万一还是两边都取消掉。 打开paypal>>找到工具>>找到批准付款 显示的…

uniapp 小程序 textarea 层级穿透,聚焦光标位置错误怎么办?

前言 在开发微信小程序时&#xff0c;使用 textarea 组件可能会遇到一些棘手的问题。最近我在使用 uniapp 开发微信小程序时&#xff0c;就遇到了两个非常令人头疼的问题&#xff1a; 层级穿透&#xff1a;由于 textarea 是原生组件&#xff0c;任何元素都无法遮盖住它。当其…

2501C++,现代C++大大提高开发效率

提升开发效率的一些语法糖: 1.if/switch初化语句 //以前 auto*tmp parseExpression(); if(tmp!nullptr){work(); } //之后if (auto* tmp parseExpression(); tmp ! nullptr) {work(); }2.结构化绑定 std::tuple<int,string> nextToken(){return {4,"直降"…

【机器学习:十四、TensorFlow与PyTorch的对比分析】

1. 发展背景与社区支持 1.1 TensorFlow的背景与发展 TensorFlow是Google于2015年发布的开源深度学习框架&#xff0c;基于其前身DistBelief系统。作为Google大规模深度学习研究成果的延续&#xff0c;TensorFlow从一开始就定位为生产级框架&#xff0c;强调跨平台部署能力和性…

Leetcode2270:分割数组的方案数

题目描述&#xff1a; 给你一个下标从 0 开始长度为 n 的整数数组 nums 。 如果以下描述为真&#xff0c;那么 nums 在下标 i 处有一个 合法的分割 &#xff1a; 前 i 1 个元素的和 大于等于 剩下的 n - i - 1 个元素的和。下标 i 的右边 至少有一个 元素&#xff0c;也就是…