LeetCode题练习与总结:子集--78

一、题目描述

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的

子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

复制代码
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

复制代码
输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

二、解题思路

  1. 子集的定义 :对于给定的数组nums,一个子集可以通过选择数组中的元素形成,可以选择0个、1个、2个,直到nums.length个元素。例如,nums = [1,2,3],可以选择[](没有元素),[1][2][3][1,2][1,3][2,3][1,2,3]

  2. 递归回溯:我们可以使用递归函数来遍历所有可能的子集。对于每个元素,我们有两个选择:将它包含在当前子集中或者不包含。这样,对于每个元素,我们可以生成两个分支:一个包含该元素,一个不包含。

  3. 终止条件:当我们的递归到达数组的末尾时,我们就可以停止递归,因为再往后就没有元素可以选择了。

  4. 去重:题目指出数组中的元素互不相同,因此我们不需要担心会有重复的子集。

三、具体代码

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        backtrack(result, new ArrayList<>(), nums, 0);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] nums, int start) {
        // 将当前子集添加到结果中,注意需要新建一个list,因为tempList会在后续递归中改变
        result.add(new ArrayList<>(tempList));
        
        for (int i = start; i < nums.length; i++) {
            // 做选择,将nums[i]添加到当前子集中
            tempList.add(nums[i]);
            // 进入下一层决策树
            backtrack(result, tempList, nums, i + 1);
            // 撤销选择,将nums[i]从当前子集中移除,尝试下一个可能的分支
            tempList.remove(tempList.size() - 1);
        }
    }
    
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {1, 2, 3};
        List<List<Integer>> subsets = solution.subsets(nums);
        for (List<Integer> subset : subsets) {
            System.out.println(subset);
        }
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 对于每个元素,我们都有两个选择:包含它或者不包含它。
  • 因此,对于n个元素的数组,我们将有2^n个可能的子集。
  • 每个子集都会被创建并添加到结果列表中一次。
  • 所以,时间复杂度是O(2^n)
2. 空间复杂度
  • 空间复杂度主要取决于递归栈的深度以及存储结果的空间。
  • 递归栈的最大深度是n,因为每次递归调用都会将一个元素添加到当前子集中,直到数组末尾。
  • 存储结果的空间是O(2^n),因为我们需要存储2^n个子集。
  • 每个子集的平均长度是n/2,所以存储所有子集的空间复杂度是O(n * 2^n)
  • 但是,如果我们只考虑额外的空间复杂度,即除了输入之外的空间复杂度,那么它是O(2^n)

五、总结知识点

  1. 回溯算法:这是一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化丢弃该解,即回溯并且再次尝试。

  2. 递归 :代码中使用了递归函数backtrack,它用于遍历所有可能的子集。递归允许函数调用自身,这在这个问题中用于深入探索决策树。

  3. 列表(List)的使用 :代码中使用了ArrayList来存储结果集result和当前子集tempList。列表是Java集合框架中的一部分,用于存储动态大小的元素集合。

  4. 列表的浅拷贝与深拷贝 :在backtrack函数中,使用new ArrayList<>(tempList)创建了一个新的列表,这是一个浅拷贝。这样做是因为tempList会在递归中改变,而我们需要保留每次递归调用前的状态。

  5. 循环(for循环) :在backtrack函数中使用了一个for循环来遍历数组nums中的元素,以构建所有可能的子集。

  6. 函数参数传递:代码中演示了如何通过函数参数传递列表和其他数据类型。在Java中,对象(包括列表)是通过引用传递的,而基本数据类型(如整数)是通过值传递的。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

相关推荐
特立独行的猫a5 分钟前
11款常用C++在线编译与运行平台推荐与对比
java·开发语言·c++
louisgeek15 分钟前
Java 位运算
java
绝无仅有43 分钟前
企微审批对接错误与解决方案
后端·算法·架构
hweiyu001 小时前
Maven 私库
java·maven
Super Rookie1 小时前
Spring Boot 企业项目技术选型
java·spring boot·后端
写不出来就跑路1 小时前
Spring Security架构与实战全解析
java·spring·架构
用户5040827858392 小时前
1. RAG 权威指南:从本地实现到生产级优化的全面实践
算法
ZeroNews内网穿透2 小时前
服装零售企业跨区域运营难题破解方案
java·大数据·运维·服务器·数据库·tcp/ip·零售
sleepcattt2 小时前
Spring中Bean的实例化(xml)
xml·java·spring