|
5 | 5 |
|
6 | 6 | ## 题目大意
|
7 | 7 |
|
8 |
| -给定一个整数数组 nums,数组中的元素互不相同,返回该数组所有可能的不重复子集。 |
| 8 | +**描述**:给定一个整数数组 `nums`,数组中的元素互不相同。 |
| 9 | + |
| 10 | +**要求**:返回该数组所有可能的不重复子集。 |
| 11 | + |
| 12 | +**示例**: |
| 13 | + |
| 14 | +```Python |
| 15 | +输入 nums = [1,2,3] |
| 16 | +输出 [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] |
| 17 | +``` |
9 | 18 |
|
10 | 19 | ## 解题思路
|
11 | 20 |
|
12 |
| -回溯算法,遍历数组 nums。为了使得子集不重复,每次遍历从当前位置的下一个位置进行下一层遍历。 |
| 21 | +### 思路 1:回溯算法 |
| 22 | + |
| 23 | +数组的每个元素都有两个选择:选与不选。 |
| 24 | + |
| 25 | +我们可以通过向当前子集数组中添加可选元素来表示选择该元素。也可以在当前递归结束之后,将之前添加的元素从当前子集数组中移除(也就是回溯)来表示不选择该元素。 |
| 26 | + |
| 27 | +回溯算法解决这道题的步骤如下: |
| 28 | + |
| 29 | +- 从第 `0` 个位置开始,调用 `backtrack` 方法进行深度优先搜索。 |
| 30 | +- 将当前子集数组 `sub_set` 添加到答案数组 `sub_sets` 中。 |
| 31 | +- 然后从当前位置开始,到数组结束为止,枚举出所有可选的元素。对于每一个可选元素: |
| 32 | + - 将其添加到当前子集数组 `sub_set` 中。 |
| 33 | + - 在选择该元素的情况下,继续递归考虑下一个元素。 |
| 34 | + - 进行回溯,撤销选择该元素。即从当前子集数组 `sub_set` 中移除之前添加的元素。 |
| 35 | + |
| 36 | +### 思路 2:二进制枚举 |
| 37 | + |
| 38 | +对于一个元素个数为 `n` 的集合 `nums` 来说,每一个位置上的元素都有选取和未选取两种状态。我们可以用数字 `1` 来表示选取该元素,用数字 `0` 来表示不选取该元素。 |
| 39 | + |
| 40 | +那么我们就可以用一个长度为 `n` 的二进制数来表示集合 `nums` 或者表示 `nums` 的子集。其中二进制的每一位数都对应了集合中某一个元素的选取状态。对于集合中第 `i` 个元素(`i` 从 `0` 开始编号)来说,二进制对应位置上的 `1` 代表该元素被选取,`0` 代表该元素未被选取。 |
| 41 | + |
| 42 | +举个例子来说明一下,比如长度为 `5` 的集合 `nums = {5, 4, 3, 2, 1}`,我们可以用一个长度为 `5` 的二进制数来表示该集合。 |
| 43 | + |
| 44 | +比如二进制数 `11111` 就表示选取集合的第 `0` 位、第 `1` 位、第 `2` 位、第 `3` 位、第 `4` 位元素,也就是集合 `{5, 4, 3, 2, 1}` ,即集合 `nums` 本身。如下表所示: |
| 45 | + |
| 46 | +| 集合 nums 对应位置(下标) | 4 | 3 | 2 | 1 | 0 | |
| 47 | +| :------------------------- | :--: | :--: | :--: | :--: | :--: | |
| 48 | +| 对应选取状态 | 选取 | 选取 | 选取 | 选取 | 选取 | |
| 49 | +| 二进制数对应位数 | 1 | 1 | 1 | 1 | 1 | |
| 50 | + |
| 51 | +再比如二进制数 `10101` 就表示选取集合的第 `0` 位、第 `2` 位、第 `5` 位元素,也就是集合 `{5, 3, 1}`。如下表所示: |
| 52 | + |
| 53 | +| 集合 nums 对应位置(下标) | 4 | 3 | 2 | 1 | 0 | |
| 54 | +| :------------------------- | :--: | :----: | :--: | :----: | :--: | |
| 55 | +| 对应选取状态 | 选取 | 未选取 | 选取 | 未选取 | 选取 | |
| 56 | +| 二进制数对应位数 | 1 | 0 | 1 | 0 | 1 | |
| 57 | + |
| 58 | +再比如二进制数 `01001` 就表示选取集合的第 `0` 位、第 `3` 位元素,也就是集合 `{5, 2}`。如下标所示: |
| 59 | + |
| 60 | +| 集合 nums 对应位置(下标) | 4 | 3 | 2 | 1 | 0 | |
| 61 | +| :------------------------- | :----: | :--: | :----: | :----: | :--: | |
| 62 | +| 对应选取状态 | 未选取 | 选取 | 未选取 | 未选取 | 选取 | |
| 63 | +| 二进制数对应位数 | 0 | 1 | 0 | 0 | 1 | |
| 64 | + |
| 65 | +通过上面的例子我们可以得到启发:对于长度为 `5` 的集合 `nums` 来说,我们只需要从 `00000` ~ `11111` 枚举一次(对应十进制为 $0 \sim 2^4 - 1$)即可得到长度为 `5` 的集合 `S` 的所有子集。 |
| 66 | + |
| 67 | +我们将上面的例子拓展到长度为 `n` 的集合 `nums`。可以总结为: |
| 68 | + |
| 69 | +- 对于长度为 `5` 的集合 `nums` 来说,只需要枚举 $0 \sim 2^n - 1$(共 $2^n$ 种情况),即可得到所有的子集。 |
13 | 70 |
|
14 | 71 | ## 代码
|
15 | 72 |
|
| 73 | +### 思路 1 :回溯算法代码 |
| 74 | + |
| 75 | +```Python |
| 76 | +class Solution: |
| 77 | + def backtrack(self, sub_sets, nums, sub_set, start): |
| 78 | + sub_sets.append(sub_set[:]) |
| 79 | + |
| 80 | + for i in range(start, len(nums)): |
| 81 | + sub_set.append(nums[i]) |
| 82 | + self.backtrack(sub_sets, nums, sub_set, i + 1) |
| 83 | + sub_set.pop() |
| 84 | + return sub_sets |
| 85 | + |
| 86 | + def subsets(self, nums: List[int]) -> List[List[int]]: |
| 87 | + if not nums: |
| 88 | + return [] |
| 89 | + sub_sets = [] |
| 90 | + return self.backtrack(sub_sets, nums, [], 0) |
| 91 | +``` |
| 92 | + |
| 93 | +### 思路 2:二进制枚举代码 |
| 94 | + |
16 | 95 | ```Python
|
17 | 96 | class Solution:
|
18 | 97 | def subsets(self, nums: List[int]) -> List[List[int]]:
|
19 |
| - def backtrack(size, subset, index): |
20 |
| - res.append(subset) |
21 |
| - for i in range(index, size): |
22 |
| - backtrack(size, subset + [nums[i]], i + 1) |
23 |
| - |
24 |
| - size = len(nums) |
25 |
| - res = list() |
26 |
| - backtrack(size, [], 0) |
27 |
| - return res |
| 98 | + n = len(nums) # n 为集合 nums 的元素个数 |
| 99 | + sub_sets = [] # sub_sets 用于保存所有子集 |
| 100 | + for i in range(1 << n): # 枚举 0 ~ 2^n - 1 |
| 101 | + sub_set = [] # sub_set 用于保存当前子集 |
| 102 | + for j in range(n): # 枚举第 i 位元素 |
| 103 | + if i >> j & 1: # 如果第 i 为元素对应二进制位删改为 1,则表示选取该元素 |
| 104 | + sub_set.append(nums[j]) # 将选取的元素加入到子集 sub_set 中 |
| 105 | + sub_sets.append(sub_set) # 将子集 sub_set 加入到所有子集数组 sub_sets 中 |
| 106 | + return sub_sets # 返回所有子集 |
28 | 107 | ```
|
29 | 108 |
|
0 commit comments