子集

题目描述

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

**说明:**解集不能包含重复的子集。

示例:

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

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

方法一

已知子集一定有空集

每增加一个元素则子集个数翻倍,其加倍的就是在原有的子集后面添加该元素形成的新的子集

例如 {a, b, c}的子集必有空集 {}

遍历到a时形成 {}, {a}

遍历到b时形成 {}, {a}, {b}, {a, b},其就是在上一行的子集中后面添加了b形成了新的子集

遍历到c时形成 {}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {a, b, c},同理也是在已有的子集后面添加了c形成新的子集

实现代码

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        ans.add(new ArrayList<>()); //添加空集
        for (int v:nums) {
            int lenght = ans.size();
            for(int i = 0;i<lenght; i++){
                List<Integer> tmp = new ArrayList<Integer>(ans.get(i)); //获取已有的子集
                tmp.add(v); //已有的子集后添加新的元素,形成新子集
                ans.add(tmp); //增加子集
            }
        }
        return ans;
    }
}

方法二

子集中对于每一个元素有存在和不存在两种状态,所以子集的个数有2^n个

如果以1表示该元素在子集中,0表示该元素不在子集中,其n个元素的子集对应的所有情况可以用1~2^n的数的二进制表示

例如 {a, b, c}的子集情况有

二进制情况对应子集情况
000{}
001{c}
010{b}
011{b, c}
100{a}
101{a, c}
110{a, b}
111{a, b, c}

实现代码

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int numsSize = nums.size();
        int setSize = pow(2,numsSize);
        vector<vector<int>> ans;
        for(unsigned int i=0;i<setSize;i++){
            vector<int>tmp;
            for(unsigned int j=0;j<numsSize;j++){
                unsigned int flag = (unsigned int)1<<j;
                if(flag&i){
                    tmp.push_back(nums[j]);
                }
            }
            ans.push_back(tmp);
        }
        return ans;
    }
};
### 子集树的概念 子集树是一种用于表示集合 \( S \) 的所有可能子集数据结构[^1]。当解决一个需要从 \( n \) 个元素中找到满足特定条件的子集的问题时,其对应的解空间被称为子集树。这种数据结构通常通过递归和回溯技术来实现。 子集树的时间复杂度为 \( O(2^n) \),这是因为对于每一个元素都有两种可能性:要么被选入当前子集中,要么不被选入。因此,总的组合数为 \( 2^n \) 种情况。 --- ### 子集树的实现方法 以下是基于 Java 编程语言的一个简单实现例子: #### 基本代码框架 ```java public class SubSetTree { public static void main(String[] args) { int[] arr = {1, 2, 3}; backtrace(arr, new boolean[arr.length], 0); } public static void backtrace(int[] arr, boolean[] selected, int index) { if (index == arr.length) { printSubset(arr, selected); } else { // 不选择第 index 个元素 selected[index] = false; backtrace(arr, selected, index + 1); // 选择第 index 个元素 selected[index] = true; backtrace(arr, selected, index + 1); } } private static void printSubset(int[] arr, boolean[] selected) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (selected[i]) { sb.append(arr[i]).append(" "); } } System.out.println(sb.toString().trim()); } } ``` 上述代码展示了如何利用递归和回溯的方法生成给定数组的所有子集。`backtrace` 函数负责探索每一种可能的选择状态,并记录哪些元素被选入子集中。最终调用 `printSubset` 方法打印出所有的子集。 --- ### 提高效率的方式 为了优化性能,在实际应用中可以通过引入 **剪枝操作** 来减少不必要的计算分支[^4]。例如,在求解 0-1 背包问题时,如果某个节点已经无法达到更优的目标值,则可以直接跳过该节点及其后续的所有子节点。 此外,还可以加入额外参数(如计数器 count),以便于控制所选出子集的具体规模或其他约束条件。 --- ### 时间与空间复杂度分析 - **时间复杂度**: 如前所述,由于存在 \( 2^n \) 种不同的子集组合形式,故整体运行时间为指数级别 \( O(2^n) \)。 - **空间复杂度**: 主要由递归栈决定,最大深度等于输入列表长度 \( n \),所以辅助存储需求大约也是线性的 \( O(n) \)[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值