回溯专题其五(棋盘篇)
一,棋盘类问题解题思路
棋盘类问题是回溯专题中的一个重要分支。与子集、组合、排列类问题不同,棋盘类问题往往需要满足额外的约束条件,即在棋盘上某个位置放置元素时,必须确保不会破坏题目定义的规则。
常见的棋盘类问题包括:
- N皇后:放置皇后时,不能在同一行、同一列或对角线上出现冲突。
- 数独:填充数字时,必须满足行、列、3x3 小方格内不能重复。
棋盘类问题的核心解题框架通常为:
-
棋盘遍历
- 按照一定的顺序(逐行逐列 / 遍历空格列表)尝试放置元素。
-
合法性判断(剪枝)
- 在放置元素前,检查是否满足题目条件(如行/列/对角线是否安全,数独是否冲突)。
- 剪枝是棋盘类问题效率的关键。
-
递归 + 回溯
- 如果当前位置合法,则尝试放置元素并进入下一步递归;
- 若递归失败,撤销本次选择(回溯),继续尝试其他可能性。
-
优化手段
- 提前记录状态(如用集合或数组存储当前行/列/方格中的元素),避免每次重复遍历。
- 排序搜索空间(如数独优先填候选数最少的格子),减少搜索分支。
二,具体代码实现
Leetcode题单:
N皇后 51
解数独 37
1. N皇后 51
N皇后问题即为在一个 n*n 的棋盘上放置n个皇后,使得这些皇后不会彼此攻击。每一个皇后的攻击范围为:
- 横向
- 纵向
- 左上到右下
- 右上到左下
给定一个整数n,要求返回n皇后问题的所有解。棋盘中用 Q 代表皇后,用 . 代表空位。
思路:
- 使用回溯逐行放置皇后;
- 每放置一个皇后,先检查当前列和两个对角线是否符合要求;
- 回溯时撤销当前选择,继续尝试下一列。
- 当放置完所有行时,将当前棋盘加入结果集;
具体代码实现:
class Solution:
def isValid(self, n: int, row: int, col: int, board: List[str]) -> bool:
# 检验同一列上是否已经有皇后存在
for i in range(row):
if board[i][col] == 'Q':
return False
# 对角线
i, j, k, l = row-1, col-1, row-1, col+1
while i>=0 and j>=0:
if board[i][j] == 'Q':
return False
i-=1
j-=1
while k>=0 and l<n:
if board[k][l] == 'Q':
return False
k-=1
l+=1
return True
def backtracking(self, n: int, row: int, board: List[str]) -> None:
if row == n:
self.result.append(board.copy())
return
for col in range(n):
# 先对当前位置进行判断,只有当前位置可以放置皇后时才放置
if not self.isValid(n, row, col, board):
continue
board[row] = board[row][:col] + 'Q' + board[row][col+1:]
self.backtracking(n, row+1, board)
board[row] = board[row][:col] + '.' + board[row][col+1:]
def solveNQueens(self, n: int) -> List[List[str]]:
self.result = []
board = ['.' * n for _ in range(n)]
self.backtracking(n, 0, board)
return self.result
2. 解数独 37
给定一个数独棋盘,要求填充其中的所有空格。
思路:
- 使用回溯逐个空格尝试填入数字。
- 通过
rows、cols、squares三个集合提前记录状态,避免重复扫描整个棋盘。 - 对空格列表进行一次静态排序,优先填候选数较少的格子,提高效率。
具体代码实现:
class Solution:
def isValid(self, row: int, col: int, num: str) -> bool:
# 同一行,同一列,3x3方格内不能出现重复数字
if num in self.rows[row] or num in self.cols[col] or num in self.squares[(row // 3) * 3 + col // 3]:
return False
return True
def backtracking(self, board: List[List[str]], idx: int) -> bool:
# 当处理完所有空格时,终止递归
if idx == len(self.empties):
return True
row, col = self.empties[idx]
square = (row//3) * 3 + (col // 3)
for candidate in map(str, range(1, 10)):
if self.isValid(row, col, candidate):
board[row][col] = candidate
self.rows[row].add(candidate)
self.cols[col].add(candidate)
self.squares[square].add(candidate)
if self.backtracking(board, idx+1):
return True
board[row][col] = '.'
self.rows[row].remove(candidate)
self.cols[col].remove(candidate)
self.squares[square].remove(candidate)
return False
def solveSudoku(self, board: List[List[str]]) -> None:
self.rows = [set() for _ in range(9)]
self.cols = [set() for _ in range(9)]
self.squares = [set() for _ in range(9)]
self.empties = []
# 初始化棋盘
for i in range(9):
for j in range(9):
if board[i][j] == '.':
self.empties.append((i, j))
else:
val = board[i][j]
self.rows[i].add(val)
self.cols[j].add(val)
self.squares[(i // 3)*3 + j // 3].add(val)
# 可选,对empties做一次简单的静态排序,优先处理候选填入元素较少的格子
def candidateSort(pos):
row, col = pos
count = 0
for candidate in map(str, range(1, 10)):
if self.isValid(row, col, candidate):
count += 1
return count
self.empties.sort(key=candidateSort)
self.backtracking(board, 0)
三,小结
棋盘类问题的本质是 在二维搜索空间中满足特定的约束条件。
与子集、组合、排列类问题相比,棋盘类问题更强调 合法性判断 和 剪枝策略。
- 在 N皇后 中,核心是“行列对角线不能冲突”,通过逐行放置 + 剪枝实现。
- 在 数独 中,核心是“行/列/方格三重约束”,通过状态记录 + 空格排序提高效率。
可以看到,虽然棋盘类问题表面复杂,但其背后依然遵循选择 -> 验证 -> 递归 -> 回溯的通用框架。

浙公网安备 33010602011771号