78. 子集
题目:
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明: 解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
(1)回溯1
递归遍历每一位,每一位都可以选或者不选,选了之后回来需要回溯的变回原样。
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtrack(int[] nums, int start) {
if(start == nums.length) {
res.add(new ArrayList<>(path));
return;
}
backtrack(nums, start + 1); //不选
path.add(nums[start]); //选
backtrack(nums, start + 1);
path.remove(path.size() - 1); //回溯复原
}
public List<List<Integer>> subsets(int[] nums) {
backtrack(nums, 0);
return res;
}
}
复杂度分析
-
时间复杂度: O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)
每个位置都可以选或不选,两种情况为 O ( 2 n ) O(2 ^ n) O(2n),添加到结果中需要 O ( n ) O(n) O(n), 所以总时间复杂度为 O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)。
-
空间复杂度: O ( n ) O(n) O(n)
构造子集需要 O ( n ) O(n) O(n)。
(2)回溯2
递归遍历每一位,递归每一层之前都先保存下路径表示当前这层的数字不选,整个路径就是全部子集。
class Solution {
//
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
public void backtrack(int[] nums, int start) {
res.add(new ArrayList<>(path));
for (int i = start; i < nums.length; i++) {
path.add(nums[i]);
backtrack(nums, i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> subsets(int[] nums) {
backtrack(nums, 0);
return res;
}
}
复杂度分析
-
时间复杂度: O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)
每个位置都可以选或不选,两种情况为 O ( 2 n ) O(2 ^ n) O(2n),添加到结果中需要 O ( n ) O(n) O(n), 所以总时间复杂度为 O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)。
-
空间复杂度: O ( n ) O(n) O(n)
构造子集需要 O ( n ) O(n) O(n)。
(3)二叉树前序遍历
每个元素都可以选或者不选,构成了一颗满二叉树。使用先序遍历即可。
class Solution {
List<List<Integer>> res = new ArrayList<>();
public void preOrder(int[] nums, List<Integer> path, int index) {
if(index >= nums.length) return;
path = new ArrayList<>(path);
res.add(new ArrayList<>(path));
preOrder(nums, path, index + 1);
path.add(nums[index]);
preOrder(nums, path, index + 1);
}
public List<List<Integer>> subsets(int[] nums) {
preOrder(nums, new ArrayList<>(), 0);
return res;
}
}
复杂度分析
-
时间复杂度: O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)
每个位置都可以选或不选,两种情况为 O ( 2 n ) O(2 ^ n) O(2n),添加到结果中需要 O ( n ) O(n) O(n), 所以总时间复杂度为 O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)。
-
空间复杂度: O ( n ) O(n) O(n)
构造子集需要 O ( n ) O(n) O(n)。
(4)位运算
每一位都可以选或者不选。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < (1 << nums.length); i++) {
List<Integer> path = new ArrayList<>();
for (int j = 0; j < nums.length; j++) {
if(((i >> j) & 1) == 1) path.add(nums[j]);
}
res.add(new ArrayList<>(path));
}
return res;
}
}
复杂度分析
-
时间复杂度: O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)
每个位置都可以选或不选,两种情况为 O ( 2 n ) O(2 ^ n) O(2n),添加到结果中需要 O ( n ) O(n) O(n), 所以总时间复杂度为 O ( n × 2 n ) O(n \times 2 ^ n) O(n×2n)。
-
空间复杂度: O ( n ) O(n) O(n)
构造子集需要 O ( n ) O(n) O(n)。