一、题设
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n
个皇后放置在 n×n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
二、基本思路
N皇后这种类似的棋盘问题,回溯即可:
首先我们先写回溯算法,回溯的模板如下:
def backtracking(参数):
if 终止条件: # 达到叶子节点
收集数据
return
# 单层逻辑
for i in range(lens):#遍历数据集
处理节点
backtracking(参数)
撤销处理节点的操作
(1)于是我们先确定终止条件:当填入n个皇后了就是产生了一个结果,将它收集.故终止条件为:if 某一回溯参数 == n(皇后个数) . 则res.append(...) return
(2)第二步,我们要确定单层逻辑:起初我想的是两层循环遍历每一个数组,这样写时间复杂度就高了,而且可能回不了。于是最核心的一个思路出来了,就是用一个全局的数组去记录每行的皇后所在位置,比如说flag[i] = j ,就代表第i行的皇后在第j列,其他没有皇后的位置就是'.',也可以暂时标记为-1.有个这个思想,我们就不用循环行了,而且还可以把行数当作回溯算法的一个参数传进来,这样不仅解决了终止条件,还减少了循环趟数。由此,我们循环第i行(i为传入参数)的每列,通过isValid()函数判断当前的(i,j)位置是否可以填入;若可填入则修改全局数组的值标记皇后位置,然后进行回溯.回溯之后的撤销操作即将皇后位置抹去。
(3)第三步是去写isValid()函数:我们先从[0,row)遍历之前的皇后,如果flag[i] == col(有已标记皇后和当前皇后在一列),或者flag[i] + (row - i) == col(有已标记皇后和当前皇后在主对角线),或者flag[i] - (row - i) == col(有已标记皇后和当前皇后在副对角线),在同一行不用判断,因为我只循环列即一行填一次。
那么关键就在于主副对角线上的判断条件怎么写,元素在主副对角线上即为横纵坐标差之比为1(主对角线),为-1(副对角线),则有:
(figure1:主对角线关系) (figure2:副对角线关系)
于是就有:
if flag[i] == col or flag[i] + (row-i) == col or flag[i] - (row - i) == col:
return False
(4)第四步就是将存储好的一个数字列表转化成字符串啦,在这里就是用generateString()这一函数实现的.就是将全局标记数组中-1值的位置转化为'.',有非-1值得地方转化为'Q'即可。这里要注意的是,我起初想直接这样赋值:
for row in range(n):
s = ""
for j in range(n):
if j == flag[row]:
s += 'Q'
else:
s += '.'
以上是没毛病的,但是我在看评论的时候我看到了另一种赋值的方法,即第一遍全部填'.',第二遍直接利用索引填'Q',即这样:
for row in range(n):
s = ""
for j in range(n):
s += '.'
s[flag[row]] = 'Q'
直接用索引就会产生报错, 故还是直接用第一种方法即可.python中的s = ""只是生成了一个可迭代的字符容器罢了,具体的大小也没有确定,所以在使用索引时就会报错;而不是像Java中的这条语句,开辟了一段固定大小的空间。故python写法的用第一种方法遍历判断即可。
char[] eachRow = new char[n]
三、代码实现
def solveNQueens(self, n):
res = [] # 用于记录最后结果
# flag记录当前行的状态
# flag[i] = j 表示第i行的皇后在j列
flag = [-1 for _ in range(n)]
# generateString函数用于结合flag中的皇后位置生成一个结果
def generateString(n):
current_res = []
for row in range(n):
s = ""
for j in range(n):
if j == flag[row]:
s += 'Q'
else:
s += '.'
current_res.append(s)
return current_res
# 判断当前(row,col)能否填入
def isValid(row,col):
current_res = True
# 遍历前几行
for i in range(row):
if flag[i] == -1:
continue
if flag[i] == col or flag[i] + (row-i) == col or flag[i] - (row - i) == col:
return False
return current_res
def backtracing(n,row):
if row == n:
res.append(generateString(n))
return
for col in range(n):# 循环每一列
# 当前位置不合法
if not isValid(row,col):
continue
# 合法,可填入
flag[row] = col
backtracing(n,row + 1)
flag[row] = -1
backtracing(n,0)
return res