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

LeetCode 78.子集

问题描述

给定一个不含重复元素的整数数组 nums,返回其所有可能的子集(幂集)。

示例

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

输出:

[ [], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3] ]

解法:回溯算法

回溯是一种 暴力搜索 方法,它通过 枚举所有可能的解 来解决问题。回溯算法的关键在于:

  1. 选择:在当前状态下选择一个可行的元素加入子集中。

  2. 递归:基于当前选择,递归探索剩余元素的子集。

  3. 回溯:撤销上一步选择,尝试其他可能。

代码实现

import java.util.*;

class Solution {
    public List<List<Integer>> ans = new ArrayList<>();
    public List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return ans;
    }
    
    public void dfs(int[] nums, int i) {
        ans.add(new ArrayList<>(path)); // 记录当前子集
        for (int j = i; j < nums.length; j++) {
            path.add(nums[j]);  // 选择元素 nums[j]
            dfs(nums, j + 1);   // 递归处理剩余元素
            path.remove(path.size() - 1);  // 回溯,撤销选择
        }
    }
}

代码解析

1. 变量定义

public List<List<Integer>> ans = new ArrayList<>();
public List<Integer> path = new ArrayList<>();
  • ans:存储所有可能的子集。

  • path:存储当前正在构造的子集。

2. 递归函数

public void dfs(int[] nums, int i) {
    ans.add(new ArrayList<>(path)); // 记录当前子集
    for (int j = i; j < nums.length; j++) {
        path.add(nums[j]);  // 选择 nums[j]
        dfs(nums, j + 1);   // 递归
        path.remove(path.size() - 1);  // 回溯
    }
}
核心逻辑
  1. 存储当前子集:进入递归时,首先将 path 加入 ans,确保所有子集都被记录。

  2. 遍历所有可能的选择

    • ji 开始,确保所有元素只能被选择一次,避免重复。

    • 选择 nums[j] 后,递归 dfs(nums, j+1) 处理剩余元素。

  3. 回溯:递归结束后,撤销上一步的选择(path.remove()),探索其他可能。

递归执行过程

假设 nums = [1,2,3],其递归树如下:

dfs(0, [])  --> 添加 []
 ├── dfs(1, [1])  --> 添加 [1]
 │   ├── dfs(2, [1,2])  --> 添加 [1,2]
 │   │   ├── dfs(3, [1,2,3])  --> 添加 [1,2,3]
 │   │   └── 回溯到 [1,2]
 │   ├── dfs(3, [1,3])  --> 添加 [1,3]
 │   └── 回溯到 [1]
 ├── dfs(2, [2])  --> 添加 [2]
 │   ├── dfs(3, [2,3])  --> 添加 [2,3]
 │   └── 回溯到 [2]
 ├── dfs(3, [3])  --> 添加 [3]
 └── 回溯到 []

最终 ans 存储的子集为:

[ [], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3] ]

时间复杂度分析

  • 每个元素可以 选或不选,因此总共有 2^n 种可能的子集。

  • 递归深度为 n,每次递归的操作为 O(1)

  • 总时间复杂度:O(2^n * n),近似 O(2^n),指数级复杂度。

总结

  1. 回溯思路

    • 递归枚举所有可能的子集。

    • 先将 path 存入 ans,然后继续选择下一个元素。

    • 递归结束后 回溯,撤销上一步的选择。

  2. 避免重复

    • ji 开始,保证每个元素只被选一次,避免重复子集。

  3. 适用场景

    • 由于时间复杂度为 O(2^n),适用于 n ≤ 20 的情况。

这个方法是解决子集问题的经典方式,希望对你有所帮助!

http://www.dtcms.com/a/102022.html

相关文章:

  • 字符函数,日期函数笔记
  • QinQ-端口安全
  • 【54】结构体:结构体指针的内存与通信
  • Qt信号槽
  • 3D设计在UI中的应用:2025年的设计新潮流!
  • OpenMCU(五):STM32F103时钟树初始化分析
  • 黑帽SEO之搜索引擎劫持-域名劫持原理分析
  • 银发浪潮下的智能护理革命:全球老龄化社会护理机器人发展研究
  • AI时代新坐标!火石会:应时而生,聚势而为
  • AI推理胜过人脑?思维模型!【34】长线思考思维模型
  • 普冉单片机PY32F002BF,使用adc内部参考电压VREFINT检测。
  • uniapp 微信小程序项目中 地图 map组件 滑动面板
  • 2025 年广东水利水电安全员考试:理论与实践融合攻略​
  • 通过redis缓存获取菜品数据
  • Docker学习--容器操作相关命令--docker events 命令
  • 【mapreduce】工作原理
  • livekit ICE连接失败的一些总结
  • 鬼泣:升龙系统
  • Linux-线程概念与线程控制的常用操作
  • 通过动态获取项目的上下文路径来确保请求的 URL 兼容两种启动方式(IDEA 启动和 Tomcat 部署)下都能正确解析
  • 深度解析算法之滑动窗口
  • 清晰易懂的Rust安装与配置教程
  • 【区块链安全 | 第十五篇】类型之值类型(二)
  • 在 RK3588 多线程推理 YOLO 时,同时开启硬件解码和 RGA 加速的性能分析
  • 【JavaEE】UDP数据报套接字编程
  • 傅里叶变换C++实现方式
  • [C++]C++入门篇 2025最新版
  • Leetcode 寻找两个正序数组的中位数
  • CAM获取操作(程序组)的加工时间
  • 短视频团队架构工作流程---2025.3.30 李劭卓