一、题目描述
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
二、题解
(1) 回溯
利用回溯方法生成子集,即对于每个元素,都有试探放入或不放入集合中的两个选择。
- 选择放入该元素,递归的进行后续元素的选择,完成放入该元素后续所有元素的试探;
- 之后将其拿出,即再进行一次选择不放入该元素,递归的进行后续元素的选择,完成不放入该元素后续所有元素的试探。
例如:元素数组:
n
u
m
s
=
[
1
,
2
,
3
,
4
,
5
,
…
]
nums = [1, 2, 3, 4, 5,…]
nums=[1,2,3,4,5,…],子集生成数组
i
t
e
m
[
]
=
[
]
item[\ ] = [\ ]
item[ ]=[ ]
对于元素
1
1
1:
- 选择放入 i t e m item item 集合, i t e m = [ 1 ] item = [1] item=[1],继续递归处理后续 [ 2 , 3 , 4 , 5 , … ] [2,3,4,5,…] [2,3,4,5,…] 元素; i t e m = [ 1 , … ] item = [1,…] item=[1,…]
- 选择不放入 i t e m item item, i t e m = [ ] item = [\ ] item=[ ],继续递归处理后续 [ 2 , 3 , 4 , 5 , … ] [2,3,4,5,…] [2,3,4,5,…] 元素; i t e m = [ … ] item = […] item=[…]
整个过程可以总结为下图:
public class Main {
public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> subset = new LinkedList<List<Integer>>();
LinkedList<Integer> item = new LinkedList<Integer>();
subset.add(item);
backtrack2(0, nums, subset, item);
return subset;
}
/**
* @date: 1/31/2020 6:27 PM
* @Execution info:1ms 击败 99.13% 的j,MB 击败 5.1% 的j
*/
private static void backtrack2(int i, int[] nums, List<List<Integer>> ans, LinkedList<Integer> item) {
if(i == nums.length)
return;
item.addLast(nums[i]);
ans.add(new LinkedList<Integer>(item));
backtrack2(i+1, nums, ans, item);
item.pollLast();
backtrack2(i+1, nums, ans, item);
}
public static void main(String[] args) {
int[] nums = {1,2,3};
System.out.println(subsets(nums));
}
}
复杂度分析
- 时间复杂度: O ( 2 n ) O(2^n) O(2n),时间差别主要在 t m p . r e m o v e ( t m p . s i z e ( ) − 1 ) ; tmp.remove(tmp.size()-1); tmp.remove(tmp.size()−1); 这里,在数据量不大的情况下, L i n k e d L i s t LinkedList LinkedList 在查找方面用到的时间比较多。
- 空间复杂度: O ( 2 n ) O(2^n) O(2n)
(2) 位运算
若一个集合有三个元素
A
,
B
,
C
A, B, C
A,B,C,则
3
3
3 个元素有
2
3
=
8
2^3 = 8
23=8 种组成集合的方式,用
0
−
7
0-7
0−7 表示这些集合。