面试题 08.12. 八皇后
设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,
也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
示例:
输入:4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
首先是一种回溯方法,当前位置合格就放入Q,回溯,删除Q。
from typing import List
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
out = []
board = [["."]*n for _ in range(n)]
def is_vaild(board,row,col, n):
for i in range(1,min(row,col)+1):
if board[row-i][col-i] == 'Q':
return False
for i in range(1,min(row+1, n-col)):
if board[row-i][col+i] == 'Q':
return False
for j in range(row):
if board[j][col] == 'Q':
return False
return True
def backtrack(board, row: int):
if row == n:
res = []
for j in board:
res.append(''.join(j))
out.append(res)
return
for col in range(n):
if not is_vaild(board,row,col,n):
continue
board[row][col] = 'Q'
backtrack(board, row+1)
board[row][col] = '.'
backtrack(board, 0)
return out
if __name__ == "__main__":
s = Solution()
print(s.solveNQueens(8))
经过提交后发现速度不是很快,因为在判断当前位置是否合法的地方存在这大量的计算,所以可以使用一个表格对已经填充过的位置进行记录。在验证的时候就不用一个一个再去判断了。
使用表格记录的回溯方法如下:建议三个表格,分别为左对角线,右对角线,竖直方向。如果有Q值记录在里面就跳出当前的循环,删除上一个Q值,进行回溯。
这种方式相对与之前的逐个比较就会快很多。
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
out = []
board = [["."]*n for _ in range(n)]
def backtrack(left_diagonal,right_diagonal,column):
row = len(column)
if row == n:
res = []
for j in board:
res.append(''.join(j))
out.append(res)
return
for col in range(n):
if col in column or (row - col) in left_diagonal or (row + col) in right_diagonal:
continue
board[row][col] = 'Q'
backtrack(left_diagonal + [row-col],right_diagonal + [row+col],column + [col])
board[row][col] = '.'
backtrack([],[],[])
return out
之后发现每次都需要计算当前column内的个数作为row的行数,所以考虑结合上面两种方法,将row代入到回溯函数中去。这样就不需每次都计算当前的行数值,减少运算。
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
out = []
board = [["."]*n for _ in range(n)]
def backtrack(board, row: int,left_diagonal,right_diagonal,column):
if row == n:
res = []
for j in board:
res.append(''.join(j))
out.append(res)
return
for col in range(n):
if col in column or (row - col) in left_diagonal or (row + col) in right_diagonal:
continue
board[row][col] = 'Q'
backtrack(board, row+1,left_diagonal + [row-col],right_diagonal + [row+col],column + [col])
board[row][col] = '.'
backtrack(board, 0,[],[],[])
return out