问题描述
n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击(不同行,不同列,不同对角线)。给定一个整数n,返回所有不同的n皇后问题的解决方案。每个解决方案包含一个明确的n皇后放置布局,其中“Q”和“.”分别表示一个女王和一个空位置。
【样例】
对于4皇后问题存在两种解决的方案:
[[".Q..", "...Q","Q...","..Q."], ["..Q.", "Q...", "...Q", ".Q.."]]
解题思路--深度遍历+回溯
- 从上到下,从左到右,判断某个位置是否可以放皇后,可以放,转2,不可以,转3;
- 放置皇后,并判断是否已经放置N个皇后,如果是,记录结果并回溯;否则转1,递归判断下一行能否放置皇后;
- 判断本行下一列是否可以放置皇后。如果本列无法放置皇后,剪枝;否则查看下一列能否放置皇后。
总结起来是:可以放置,就往下找;放不了,就往回看,看能不能继续往下找,直到第一层都试过最后一列的位置,程序结束。
递归实现
class Solution:
"""
@param: n: The number of queens
@return: All distinct solutions
"""
def solveNQueens(self, n):
# write your code here
if n <= 0: return []
matrix = []
result = []
for i in range(n):
matrix.append(["." for j in range(n)])
self.placeQueue(0, 0, matrix, result)
return result
def placeQueue(self, row, col, matrix, result):
n = len(matrix)
if row == n or col == n: return
if self.isValid(matrix, (row, col)):
matrix[row][col] = "Q"
if row == n - 1:
result.append(self.copy(matrix))
matrix[row][col] = "."
return
else:
self.placeQueue(row+1, 0, matrix, result)
matrix[row][col] = "."
self.placeQueue(row, col+1, matrix, result)
else:
if col == n-1: return
else: self.placeQueue(row, col+1, matrix, result)
def copy(self, matrix):
result = []
for i in range(len(matrix)):
result.append("".join(matrix[i]))
return result
def isValid(self, matrix, (row, col)):
n = len(matrix)
for i in range(n):
for j in range(n):
if matrix[i][j] == "Q":
if i == row or j == col or abs(row-i) == abs(col-j):
return False
return True
在网上找了相关资料,发现数据结构可以优化:
把棋盘存储为一个N维数组a[N],数组中第i个元素的值代表第i行的皇后位置,这样便可以把问题的空间规模压缩为一维O(N)。
在判断是否冲突时也很简单
1.首先每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了;
2.其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可;
3.斜线冲突,通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,即| row – i | = | col – a[i] | 。
非递归实现
使用递归方法,在n>10时,占用时间会比较长,故优化为不递归方式,同时把上述数据结构优化实现。
非递归方法的一个重要问题时何时回溯及如何回溯的问题。
首先对N行中的每一行进行探测,寻找该行中可以放置皇后的位置,具体方法是对该行的每一列进行探测,看是否可以放置皇后,如果可以,则在该列放置一个皇后,然后继续探测下一行的皇后位置。如果已经探测完所有的列都没有找到可以放置皇后的列,此时就应该回溯,把上一行皇后的位置往后移一列,如果上一行皇后移动后也找不到位置,则继续回溯直至某一行找到皇后的位置或回溯到第一行,如果第一行皇后也无法找到可以放置皇后的位置,则说明已经找到所有的解程序终止。如果该行已经是最后一行,则探测完该行后,如果找到放置皇后的位置,则说明找到一个结果,打印出来。但是此时并不能再此处结束程序,因为我们要找的是所有N皇后问题所有的解,此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测.
上代码:
class Solution:
"""
@param: n: The number of queens
@return: All distinct solutions
"""
def solveNQueens(self, n):
# write your code here
row, col = 0, 0
boardChecker = [-1 for i in range(n)]
result = []
while row < n:
while col < n:
if self.canPlace(row, col, boardChecker):
boardChecker[row] = col
col = 0
break
else:
col += 1
continue
if boardChecker[row] == -1:
if row == 0:#no solution
break
else:
row -= 1
col = boardChecker[row] + 1
boardChecker[row] = -1
continue
if row == n-1:
one_result = []
for k in range(n):
temp = ["." for i in range(n)]
if 0 <= boardChecker[k] < n:
temp[boardChecker[k]]="Q"
one_result.append("".join([item for item in temp]))
result.append([item for item in one_result])
col = boardChecker[row] + 1
boardChecker[row] = -1
continue
row += 1
return result
def canPlace(self, row, col, boardChecker):
n = len(boardChecker)
for i in [j for j in range(n)]:
if boardChecker[i] != -1:
if boardChecker[i] == col or abs(row-i) == abs(col-boardChecker[i]):
return False
return True