leetcode
leetcode 2801 ~ 2850
幂集

幂集

难度:

标签:

题目描述

Write a method to return all subsets of a set. The elements in a set are pairwise distinct.

Note: The result set should not contain duplicated subsets.

Example:

 Input:  nums = [1,2,3]
 Output: 
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

代码结果

运行时间: 24 ms, 内存: 16.1 MB


/*
 * 思路:
 * 1. 使用递归方法来生成所有子集。
 * 2. 对于每个元素,有两种选择:包含在子集中或者不包含。
 * 3. 使用Java Stream API来简化列表操作。
 */

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SubsetsStream {
    public List<List<Integer>> subsets(int[] nums) {
        return subsetsHelper(nums, 0);
    }

    private List<List<Integer>> subsetsHelper(int[] nums, int index) {
        if (index == nums.length) {
            List<List<Integer>> base = new ArrayList<>();
            base.add(new ArrayList<>());
            return base;
        }
        int num = nums[index];
        List<List<Integer>> subsets = subsetsHelper(nums, index + 1);
        List<List<Integer>> moreSubsets = subsets.stream()
                .map(ArrayList::new)
                .peek(list -> list.add(num))
                .collect(Collectors.toList());
        return Stream.concat(subsets.stream(), moreSubsets.stream()).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        SubsetsStream solution = new SubsetsStream();
        int[] nums = {1, 2, 3};
        List<List<Integer>> subsets = solution.subsets(nums);
        System.out.println(subsets);
    }
}

解释

方法:

此题解采用了深度优先搜索(DFS)的方法来生成所有子集。函数`dfs`被设计为一个递归函数,其参数`i`表示当前考虑到的元素索引,而`r`是当前已构建的子集。在每个递归调用中,有两个选择:不包括当前元素,继续递归调用`dfs(i + 1, r)`;或者包括当前元素,在子集`r`中添加`nums[i]`,然后进行递归调用`dfs(i + 1, r + [nums[i]])`。当索引`i`等于数组长度时,意味着已经考虑了所有元素,当前子集`r`被添加到答案列表`ans`中。

时间复杂度:

O(2^n)

空间复杂度:

O(n)

代码细节讲解

🦆
在DFS递归函数中,如何确保生成的子集中不会包含重复元素,尤其是当输入数组已经保证无重复元素时?
在提供的DFS递归方法中,确保不生成重复子集的关键在于输入数组`nums`本身不含重复元素,并且递归过程中对每个元素的处理是线性且顺序的。在每次递归调用中,函数要么选择包含当前元素,要么选择不包含,然后移动到下一个元素。由于每个元素在递归树的同一层只考虑一次,并且递归调用的顺序保证了每个子集都是独一无二的组合,这就避免了重复子集的生成。
🦆
为什么在到达数组长度的条件下(i == n),直接添加当前子集`r`到结果列表`ans`中,而不进行额外的检查或操作?
当递归函数的索引`i`达到数组`nums`的长度`n`时,意味着所有元素都已被考虑完成。此时的子集`r`是根据之前的选择(包含或不包含每个元素)构建的一个完整子集。由于递归过程确保了每次递归都是基于不同的元素选择进行的,因此每到达这个终止条件,我们都得到了一个有效且不重复的子集,不需要进一步的检查或操作就可以直接将其加入到结果列表`ans`中。
🦆
请解释递归调用`dfs(i + 1, r)`和`dfs(i + 1, r + [nums[i]])`在子集构建过程中的作用及其对结果集的影响。
在递归函数`dfs`中,`dfs(i + 1, r)`表示当前元素`nums[i]`不被包括在子集中,递归继续考虑下一个元素,这是构建所有可能子集的'不选'分支。而`dfs(i + 1, r + [nums[i]])`则表示当前元素`nums[i]`被包含在当前子集`r`中,然后递归继续考虑下一个元素,这是'选'分支。通过这两种路径,确保了从当前元素开始的所有可能的子集组合都被探索了,从而生成了包含所有可能元素组合的完整子集列表。这种方法有效地使用了分治策略,探索了所有包含或不包含每个特定元素的情况,从而得到了完整的幂集。

相关问题