@@ -169,17 +169,34 @@ for i in range(len(nums)): # 枚举可选元素列表
169169
170170** 描述** :给定一个整数数组 ` nums ` ,数组中的元素互不相同。
171171
172- ** 要求** :返回该数组所有可能的不重复子集。
172+ ** 要求** :返回该数组所有可能的不重复子集。可以按任意顺序返回解集。
173+
174+ ** 说明** :
175+
176+ - $1 \le nums.length \le 10$。
177+ - $-10 \le nums[ i] \le 10$。
178+ - ` nums ` 中的所有元素互不相同。
173179
174180** 示例** :
175181
182+ - 示例 1:
183+
184+ ``` Python
185+ 输入 nums = [1 ,2 ,3 ]
186+ 输出 [[],[1 ],[2 ],[1 ,2 ],[3 ],[1 ,3 ],[2 ,3 ],[1 ,2 ,3 ]]
187+ ```
188+
189+ - 示例 2:
190+
176191``` Python
177- 输入 nums = [1 , 2 , 3 ]
178- 输出 [[],[1 ],[ 2 ],[ 1 , 2 ],[ 3 ],[ 1 , 3 ],[ 2 , 3 ],[ 1 , 2 , 3 ]]
192+ 输入: nums = [0 ]
193+ 输出: [[],[0 ]]
179194```
180195
181196#### 5.1.3 解题思路
182197
198+ ##### 思路 1:回溯算法
199+
183200数组的每个元素都有两个选择:选与不选。
184201
185202我们可以通过向当前子集数组中添加可选元素来表示选择该元素。也可以在当前递归结束之后,将之前添加的元素从当前子集数组中移除(也就是回溯)来表示不选择该元素。
@@ -216,7 +233,7 @@ for i in range(len(nums)): # 枚举可选元素列表
216233 - 当遍历到决策树的叶子节点时,就终止了。也就是当正在考虑的元素位置到达数组末尾(即 `start >= len (nums)` )时,递归停止。
217234 - 从决策树中也可以看出,子集需要存储的答案集合应该包含决策树上所有的节点,应该需要保存递归搜索的所有状态。所以无论是否达到终止条件,我们都应该将当前符合条件的结果放入到集合中。
218235
219- # ### 5.1.4 代码
236+ # #### 思路 1: 代码
220237
221238```Python
222239class Solution :
@@ -237,6 +254,11 @@ class Solution:
237254 return res
238255```
239256
257+ ##### 思路 1:复杂度分析
258+
259+ - ** 时间复杂度** :$O(n \times 2^n)$,其中 $n$ 指的是数组 ` nums ` 的元素个数,$2^n$ 指的是所有状态数。每种状态需要 $O(n)$ 的时间来构造子集。
260+ - ** 空间复杂度** :$O(n)$,每种状态下构造子集需要使用 $O(n)$ 的空间。
261+
240262### 5.2 N 皇后
241263
242264#### 5.2.1 题目链接
@@ -257,16 +279,20 @@ class Solution:
257279
258280** 示例** :
259281
282+ - 示例 1:
283+
260284``` Python
261- 输入 n = 4
262- 输出 [[" .Q.." ," ...Q" ," Q..." ," ..Q." ],[" ..Q." ," Q..." ," ...Q" ," .Q.." ]]
263- 解释 如下图所示,4 皇后问题存在 2 个不同的解法。
285+ 输入: n = 4
286+ 输出: [[" .Q.." ," ...Q" ," Q..." ," ..Q." ],[" ..Q." ," Q..." ," ...Q" ," .Q.." ]]
287+ 解释: 如下图所示,4 皇后问题存在 2 个不同的解法。
264288```
265289
266290![ ] ( https://assets.leetcode.com/uploads/2020/11/13/queens.jpg )
267291
268292#### 5.2.3 解题思路
269293
294+ ##### 思路 1:回溯算法
295+
270296这道题是经典的回溯问题。我们可以按照行序来放置皇后,也就是先放第一行,再放第二行 …… 一直放到最后一行。
271297
272298对于 ` n * n ` 的棋盘来说,每一行有 ` n ` 列,也就有 ` n ` 种放法可供选择。我们可以尝试选择其中一列,查看是否与之前放置的皇后有冲突,如果没有冲突,则继续在下一行放置皇后。依次类推,直到放置完所有皇后,并且都不发生冲突时,就得到了一个合理的解。
@@ -275,7 +301,7 @@ class Solution:
275301
276302下面我们根据回溯算法三步走,写出对应的回溯算法。
277303
278- 1 . ** 明确所有选择** :根据棋盘中当前行的所有列位置上是否选择放置皇后。以 ` 3 * 3 ` 大小的棋盘为例 ,画出决策树,如下图所示。
304+ 1 . ** 明确所有选择** :根据棋盘中当前行的所有列位置上是否选择放置皇后,画出决策树,如下图所示。
279305
280306 - ![ ] ( https://qcdn.itcharge.cn/images/20220426095225.png )
281307
@@ -332,11 +358,25 @@ class Solution:
332358 - 当遍历到决策树的叶子节点时,就终止了。也就是在最后一行放置完皇后(即 `row == n` )时,递归停止。
333359 - 递归停止时,将当前符合条件的棋盘转换为答案需要的形式,然后将其存入答案数组 `res` 中即可。
334360
335- # ### 5.2.4 代码
361+ # #### 思路 1: 代码
336362
337363```Python
338364class Solution :
339- # 判断当前位置 row, col 是否与之前放置的皇后发生冲突
365+ res = []
366+ def backtrack (self , n : int , row : int , chessboard : List[List[str ]]):
367+ if row == n:
368+ temp_res = []
369+ for temp in chessboard:
370+ temp_str = ' ' .join(temp)
371+ temp_res.append(temp_str)
372+ self .res.append(temp_res)
373+ return
374+ for col in range (n):
375+ if self .isValid(n, row, col, chessboard):
376+ chessboard[row][col] = ' Q'
377+ self .backtrack(n, row + 1 , chessboard)
378+ chessboard[row][col] = ' .'
379+
340380 def isValid (self , n : int , row : int , col : int , chessboard : List[List[str ]]):
341381 for i in range (row):
342382 if chessboard[i][col] == ' Q' :
@@ -348,7 +388,6 @@ class Solution:
348388 return False
349389 i -= 1
350390 j -= 1
351-
352391 i, j = row - 1 , col + 1
353392 while i >= 0 and j < n:
354393 if chessboard[i][j] == ' Q' :
@@ -357,29 +396,19 @@ class Solution:
357396 j += 1
358397
359398 return True
360-
399+
361400 def solveNQueens (self , n : int ) -> List[List[str ]]:
362- chessboard = [[' .' for _ in range (n)] for _ in range (n)] # 棋盘初始化
401+ self .res.clear()
402+ chessboard = [[' .' for _ in range (n)] for _ in range (n)]
403+ self .backtrack(n, 0 , chessboard)
404+ return self .res
405+ ```
363406
364- res = [] # 存放所有符合条件结果的集合
365- def backtrack (chessboard : List[List[str ]], row : int ): # 正在考虑放置第 row 行的皇后
366- if row == n: # 遇到终止条件
367- path = [] # 当前符合条件的结果
368- for ch in chessboard:
369- row_str = ' ' .join(ch)
370- path.append(row_str)
371- res.append(path) # 将当前符合条件的结果放入集合中
372- return
407+ ##### 思路 1:复杂度分析
373408
374- for col in range (n): # 枚举可放置皇后的列
375- if self .isValid(n, row, col, chessboard): # 如果该位置与之前放置的皇后不发生冲突
376- chessboard[row][col] = ' Q' # 选择 row, col 位置放置皇后
377- backtrack(chessboard, row + 1 ) # 递归放置 row + 1 行之后的皇后
378- chessboard[row][col] = ' .' # 撤销选择 row, col 位置
409+ - ** 时间复杂度** :$O(n!)$,其中 $n$ 是皇后数量。
410+ - ** 空间复杂度** :$O(n^2)$,其中 $n$ 是皇后数量。递归调用层数不会超过 $n$,每个棋盘的空间复杂度为 $O(n^2)$,所以空间复杂度为 $O(n^2)$。
379411
380- backtrack(chessboard, 0 )
381- return res
382- ```
383412
384413## 参考资料
385414
0 commit comments