适用场景:
输入数据:如果是递归数据结构,如单链表,二叉树,集合,则百分之百可以用深搜;如果是非递归数据结构,如一维数组,二维数组,字符串,图,则概率小一些。
状态转换图:树或者DAG。
求解目标:多阶段存在性问题。必须要走到最深(例如对于树,必须要走到叶子节点)才能得到一个解,这种情况适合用深搜。
dfs模板:
void dfs(input, path, result, cur) {
if (数据非法) return; // 终止条件
if (cur == input.size()) { // 收敛条件
将path放入result
result.append(list(path))
}
if (可以剪枝) return;
for(step in range(cur, end)) { // 执行所有可能的扩展动作
执行动作,修改path
dfs(input, path, step + 1 or cur + 1, result);
恢复path,排列类的题目恢复交换元素
}
}
1、N皇后问题
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
def solveNQueens(n):
"""
:type n: int
:rtype: List[List[str]]
"""
# 判断是否冲突
def is_confict(cur_x, cur_y, pos):
if cur_x in pos: # 该行之前放过
return True
for y in range(len(pos)):
x = pos[y]
if abs(cur_x - x) == abs(cur_y - y): # 斜行放过
return True
return False
# 输出的格式
def format_out(pos, N):
res = [""] * N
for y in range(len(pos)):
x = pos[y]
res[x] = "." * y + "Q" + "." * (N - y - 1)
return res
def dfs(index, pos, N, res):
# pos 记录每列皇后的位置
if index == N:
res.append(format_out(pos, N))
return
for i in range(N):
if is_confict(i, index, pos):
continue
dfs(index + 1, pos + [i], N, res)
return res
return dfs(0, [], n, [])
2、数独问题
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
"""
def dfs(index, rows, clumns, boxs, board, empty):
if index == len(empty):
print("*************************")
return True
i, j = empty[index]
box_index = (i // 3) * 3 + j // 3
for num in rows[i] & clumns[j] & boxs[box_index]:
rows[i].remove(num)
clumns[j].remove(num)
boxs[box_index].remove(num)
board[i][j] = str(num)
if dfs(index+1, rows, clumns, boxs, board, empty):
return True
rows[i].add(num) # 回溯
clumns[j].add(num)
boxs[box_index].add(num)
return False
rows = [set(range(1, 10)) for i in range(9)]
clumns = [set(range(1, 10)) for i in range(9)]
boxs = [set(range(1, 10)) for i in range(9)]
empty = [] # 收集需填数位置
for i in range(9):
for j in range(9):
if board[i][j] != '.': # 更新可用数字
num = int(board[i][j])
rows[i].remove(num)
clumns[j].remove(num)
boxs[(i // 3) * 3 + j // 3].remove(num)
else:
empty.append((i, j))
dfs(0, rows, clumns, boxs, board, empty)