题目
给定一个数组,请返回所有的子集。
输入: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=1∏liN−i+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=0∑nCNl<=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=0∑Nl=0∑nCl=2(1+N)∗2N+2N=2N∗2N+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=1∑Nj=i+1∑N...q=p+1∑N1
其等价于
∏
i
=
1
l
N
−
i
+
1
i
\prod^l_{i=1} \frac{N - i + 1}{i}
i=1∏liN−i+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=1∑NCNl=2N−1
- 复杂度分析
- 时间复杂度 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();
}
}
};