Skip to content

Commit e9c7925

Browse files
committed
Update 01.Backtracking-Algorithm.md
1 parent b874cb0 commit e9c7925

File tree

1 file changed

+59
-30
lines changed

1 file changed

+59
-30
lines changed

Contents/09.Algorithm-Base/04.Backtracking-Algorithm/01.Backtracking-Algorithm.md

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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
222239
class 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
338364
class 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

Comments
 (0)