当前位置: 首页 > news >正文

【LeetCode 热题 100】78. 子集——(解法二)回溯+选哪个

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

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(N * 2^N)
    • 空间复杂度:O(N)

整体思路

这段代码同样旨在解决 “子集 (Subsets)” 问题,即找出给定数组的所有可能子集。与上一个“选或不选”的实现方式不同,此版本的回溯法思路可以理解为 构建组合 的模型。

算法的整体思路可以分解为以下步骤:

  1. 决策模型:构建组合

    • 该算法将生成子集的过程,看作是从原数组 nums 中挑选 0 个、1 个、2 个……直到 n 个元素来组成不同长度的子集。
    • 它定义了一个 dfs(i, ...) 函数,其核心任务是:从 nums 数组的第 i 个元素开始,向后选择元素,加入到当前正在构建的子集 path 中。
  2. 递归与回溯的核心逻辑

    • dfs 函数被调用时,首先要做的一件事是 将当前 path 的状态加入结果集
      • ans.add(new ArrayList<>(path)):这意味着,无论是空集(第一次调用时)、只含一个元素的子集、还是包含多个元素的子集,只要进入 dfs 函数,path 的当前状态就是一个合法的子集。这与“选或不选”模型只在叶子节点收集结果的方式不同。
    • 接下来,通过一个 for 循环来做选择。这个循环从当前位置 i 开始,而不是从 0 开始。
      • for (int j = i; j < nums.length; j++): 这个循环的作用是,在当前层级的决策中,我们可以选择 nums[i], nums[i+1], nums[i+2] 等等中的任意一个,作为下一个要加入 path 的元素。
    • 选择 (Choose)path.add(nums[j])。将选中的元素 nums[j] 加入路径。
    • 探索 (Explore)dfs(j + 1, nums, ...)。做出选择后,递归地进入下一层。注意,下一层的搜索起点是 j + 1,而不是 i + 1。这确保了我们不会重复选择同一个元素,并且生成的子集中的元素顺序是按照原数组的顺序来的(例如,不会出现 [3, 1] 这样的组合,只会是 [1, 3])。
    • 撤销选择 (Unchoose / Backtrack)path.removeLast()。当对 j + 1 的探索返回后,撤销刚才的选择,将 nums[j]path 中移除。这样,在下一次 for 循环中,就可以尝试选择 nums[j+1] 作为当前层级的选择了。
  3. 递归的启动

    • 初始调用是 dfs(0, ...),这意味着第一次我们可以在 nums[0...n-1] 中任意选择一个元素作为子集的第一个元素。

通过这种方式,DFS的每一条路径都代表了子集的构建过程,并且在进入每个递归节点时都收集一次结果,从而不重不漏地生成了所有子集。

完整代码

class Solution {/*** 计算给定数组的所有子集(幂集)。* (组合模型的回溯实现)* @param nums 不含重复元素的整数数组* @return 包含所有子集的列表*/public List<List<Integer>> subsets(int[] nums) {// ans: 最终的结果列表,用于存储所有子集List<List<Integer>> ans = new ArrayList<>();// path: 用于存储当前正在构建的单个子集路径List<Integer> path = new ArrayList<>();// 从索引 0 开始进行深度优先搜索dfs(0, nums, ans, path);return ans;}/*** 深度优先搜索(回溯)辅助函数。* @param i 当前选择的起点索引。表示在这一层,我们可以从 nums[i] 开始向后选择元素。* @param nums 原始输入数组* @param ans 结果列表* @param path 当前构建的子集*/private void dfs(int i, int[] nums, List<List<Integer>> ans, List<Integer> path) {// 关键步骤:在每个递归节点,都将当前 path 的一个深拷贝加入结果集。// 这代表了以当前 path 为前缀的所有子集中的一个。// 第一次调用时,path为空,加入的是空集。ans.add(new ArrayList<>(path));// 从当前起点 i 开始,向后遍历,尝试将每个元素加入 pathfor (int j = i; j < nums.length; j++) {// 选择 (Choose): 将 nums[j] 加入当前子集path.add(nums[j]);// 探索 (Explore): 递归地去构建更长的子集。// 注意:下一层的起点是 j + 1,确保每个元素只用一次,且组合不重复。dfs(j + 1, nums, ans, path);// 撤销选择 (Unchoose / Backtrack): 将刚刚加入的 nums[j] 移除,// 恢复到上一层状态,以便 for 循环可以尝试下一个 j。path.remove(path.size() - 1); // removeLast() is more efficient if path is a LinkedList}}
}

时空复杂度

时间复杂度:O(N * 2^N)

  1. 子集数量:此算法同样会生成 2^N 个子集。
  2. 构建每个子集的成本
    • 该算法的DFS树结构与前一个版本不同,但最终生成的子集数量和总的计算量级是相同的。
    • 在每次 dfs 调用时(总共有 2^N 次调用),都会执行一次 ans.add(new ArrayList<>(path))
    • path 的长度从 0 到 N 不等。所有 path 的总长度加起来,可以证明是 O(N * 2^N)。
    • 因此,所有复制操作的总时间消耗是 O(N * 2^N)

综合分析
尽管实现方式不同,但解决问题的根本计算量没有改变。总时间复杂度仍然是 O(N * 2^N)

空间复杂度:O(N)

  1. 主要存储开销:我们分析的是额外辅助空间,不包括存储最终结果的 ans 列表。
    • List<Integer> path: 用于存储当前路径。path 的最大长度为 N。空间复杂度为 O(N)
    • 递归调用栈dfs 函数的最大递归深度为 N (当构建包含所有 N 个元素的子集时,路径为 0 -> 1 -> ... -> N-1)。因此,递归栈所占用的空间为 O(N)

综合分析
算法所需的额外辅助空间由 path 和递归栈深度共同决定。它们都是 O(N) 级别的。因此,总的辅助空间复杂度为 O(N)

参考灵神

http://www.xdnf.cn/news/1174825.html

相关文章:

  • Unity × RTMP × 头显设备:打造沉浸式工业远控视频系统的完整方案
  • AI营销核心技术解析:运作机制与行业应用实例
  • 炬森精密:缓冲滑轨的创新力量,重塑家居静音与安全新体验
  • 力扣MySQL(1)
  • 解构未来金融:深入剖析DeFi与去中心化交易所(DEX)的技术架构
  • 力扣(LeetCode) ——轮转数组(C语言)
  • Linux 723 磁盘配额 限制用户写入 quota;snap快照原理
  • GraphQL批量查询优化:DataLoader如何让数据库访问速度飞起来?
  • Android 测试全指南:单元测试与UI测试框架详解
  • 用马尔可夫模型进行自动驾驶安全分析
  • Docker Desktop 打包Unity WebGL 程序,在Docker 中运行Unity WebGL 程序
  • 【Linux系统编程】基础指令
  • MYSQL 笔记3
  • 天津大学陈亚楠教授团队 ACS AEM:焦耳热超快合成非平衡态能源材料——毫秒级制备与跨体系性能突破
  • 2025-07-23vscode+cline使用笔记
  • springcloud环境和工程搭建
  • AI 驱动与数字化技术双突破!华南Formnext展3D 打印开启智能制造新场景
  • 基于Seata的微服务分布式事务实战经验分享
  • 策略模式(Strategy Pattern)+ 模板方法模式(Template Method Pattern)的组合使用
  • android studio打包vue
  • 如何硬解析 .shp 文件中的几何体,拯救 .dbf、.shx 等文件缺失的 ESRI Shapefile 格式文件
  • .Net core 部署到IIS出现500.19Internal Server Error 解决方法
  • 变频器实习DAY12
  • VRRP技术-设备备份技术
  • Vue3 面试题及详细答案120道(61-75 )
  • Mermaid流程图
  • 思路探索:当大型语言模型遇见数据分析的现实挑战
  • vue3与ue5通信-工具类
  • 【音视频学习】四、深入解析视频技术中的YUV数据存储方式:从原理到实践
  • Kubernetes 服务发布进阶