leetcode hot100 之 子集

题目

给定一个数组,请返回所有的子集。

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

原题链接:https://leetcode-cn.com/problems/subsets/

思路

思路1

假定数组长度为 N,首先外层循环,先定好每次选 n 个数,然后回溯法从第 0 个数开始选择即可,直到选满 n 个数为止。为了结果不重复,每次选择第 x 个数后,只从 x + 1 之后的数进行选择。同时,提前做剪枝,如果剩余的数字不够填满子结果了,就提前 break。
每次选 n 个数时,在不考虑剪枝的情况下,回溯的过程中,除了第0层外,第 l 层(下标从1开始)的复杂度为(推导略麻烦)
∏ i = 1 l N − i + 1 i \prod^l_{i=1} \frac{N - i + 1}{i} i=1liNi+1
这个实际上就是
C N l C^l_N CNl

那么整体选 n 个数的复杂度为
∑ l = 0 n C N l < = 2 N \sum^n_{l=0} C^l_N <= 2^N l=0nCNl<=2N

再把外循环加进来,整体就是:
∑ n = 0 N ∑ l = 0 n C l = ( 1 + N ) ∗ 2 N + 2 N 2 = N ∗ 2 N 2 + 2 N \sum^N_{n=0}\sum^n_{l=0} C^l = \frac{(1+N)*2^N + 2^N}{2}=\frac{N*2^N}{2}+2^N n=0Nl=0nCl=2(1+N)2N+2N=2N2N+2N

  • 复杂度分析
    • 时间复杂度 O(N * 2^N)。根据上面的复杂度推导。
    • 空间复杂度 O(N) 。回溯最多N层,临时子结果最大为N。
思路2

思路1 当中有一个外循环的过程,也就是先固定数组长度 n 后,再进行回溯。实际上,在进行长度为 N 的回溯过程中,result 子结果会把所有的遍历情况都经历过一次,所以实际上,外循环一次即可。可以再回溯的过程中,把每一个 result 子结果 push 到 results 当中。
不过这种方式就没有剪枝的情况,所以复杂度和思路1 基本上是一样的。具体就不推导了。

选 N 个数时,回溯的过程中,除了第0层外,第 l 层(下标从1开始)的复杂度为
∑ i = 1 N ∑ j = i + 1 N . . . ∑ q = p + 1 N 1 \sum^N_{i=1} \sum^N_{j=i+1}...\sum^N_{q=p+1}1 i=1Nj=i+1N...q=p+1N1
其等价于
∏ i = 1 l N − i + 1 i \prod^l_{i=1} \frac{N - i + 1}{i} i=1liNi+1
这个实际上就是
C N l C^l_N CNl

那么整体选 N 个数的复杂度为
∑ l = 1 N C N l = 2 N − 1 \sum^N_{l=1} C^l_N = 2^N - 1 l=1NCNl=2N1

  • 复杂度分析
    • 时间复杂度 O(2^N)。
    • 空间复杂度 O(N) 。

代码

代码1
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> results;
        vector<int> result;
        for (int i = 0; i <= nums.size(); i++) {
            backtrack(results, result, nums, i, 0);
        }
        return results;
    }
    void backtrack(vector<vector<int>>& results,
                   vector<int>& result, 
                   vector<int>& nums, 
                   int length, 
                   int index) {
        if (result.size() == length) {
            results.push_back(result);
            return;
        }

        for (int i = index; i < nums.size(); i++) {
            if (length - result.size() > nums.size() - index) {
                break;
            }
            result.push_back(nums[i]);
            backtrack(results, result, nums, length, i + 1);
            result.pop_back();
        }
    }
};
代码2
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> results;
        vector<int> result;
        results.push_back({});
        backtrack(results, result, nums, nums.size(), 0);
        return results;
    }
    void backtrack(vector<vector<int>>& results,
                   vector<int>& result, 
                   vector<int>& nums, 
                   int length, 
                   int index) {
        if (result.size() == length) {
            return;
        }
        for (int i = index; i < nums.size(); i++) {
            result.push_back(nums[i]);     
            results.push_back(result);
            backtrack(results, result, nums, length, i + 1);
            result.pop_back();
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值