Leetcode Link: 51. N 皇后 - 力扣(LeetCode)

题目

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

解法一

思路:属于回溯问题中的棋盘问题,收集的是满足条件的叶子节点。

满足题目条件 能够走到叶子节点就可以满足条件(不满足问题条件会被剪枝)

剪枝条件 当前位置放置皇后同一行有其他皇后 同一列有其他皇后 正斜线上有其他皇后 反斜线上有其他皇后

停止回溯条件 走到叶子节点就可以停止了 若不满足防止皇后条件,是 continue 而不是 return

题解

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        def backtracking(i):
	        # 放置第i行的皇后
            nonlocal checkboard, res
            # 满足题目要求,停止回溯
            if i == self.n:
                res.append(checkboard.copy())
                return
            
            # 遍历当前的每一列
            for j in range(self.n):
                # 准备位置[i,j]放个皇后,不满足放置条件则跳过
                # 注意这里都还没改变 checkboard 呢,满足条件再改
                if not self.check(checkboard, i, j):
                    continue
                checkboard[i] = checkboard[i][:j]+'Q'+checkboard[i][j+1:]
                backtracking(i+1)
                checkboard[i] = checkboard[i][:j]+'.'+checkboard[i][j+1:]
        self.n = n
        res = []
        checkboard = ['.'*n for _ in range(self.n)]
        backtracking(0)
        return res
        
	# 判定当前放置点是否合格
    def check(self, checkboard, i, j):
        ### 同一列上有Q (只往上看)
        row = i
        while(1):
            row -= 1
            if row < 0: break
            if checkboard[row][j] == 'Q':
                return False
        ### 左上对角线有Q:
        row = i
        col = j
        while(1):
            row -= 1
            col -= 1
            if row < 0 or col <0: 
                break
            if checkboard[row][col] == 'Q':
                return False
        ### 右上对角线有Q:
        row = i
        col = j
        while(1):
            row -= 1
            col += 1
            if row < 0 or col > self.n-1: 
                break
            if checkboard[row][col] == 'Q':
                return False
        return True

复习时的代码

利用一个set()来指示哪些col还可以用,不用每次都遍历了。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        res = []
        path = ["".join(['.']*n) for _ in range(n)]
        valid_col = set([i for i in range(n)])
        def process(i):
            nonlocal valid_col
            if i == n:
                res.append(path.copy())
            for j in range(n):
                if j not in valid_col: continue
                if self.check_diag(i,j,path):
                    valid_col.remove(j)
                    path[i] = path[i][:j]+'Q'+path[i][j+1:]
                    process(i+1)
                    path[i] = path[i][:j]+'.'+path[i][j+1:]
                    valid_col.add(j)
        process(0)
        return res
    # 对角线检查
    def check_diag(self, i, j, path):
        bkp_i = i
        bkp_j = j
        while(i>=0 and j>=0):
            if path[i][j] == 'Q':
                return False
            else:
                i -= 1
                j -= 1
        i = bkp_i
        j = bkp_j
        while(i>=0 and j<len(path[0])):
            if path[i][j] == 'Q':
                return False
            else:
                i -= 1
                j += 1
        
        return True
    
 

启发和联系

  1. 由于字符串在 python 中是不可变变量,因此每一行放置/删除皇后的的语句相当于重新赋值
# 放置皇后
checkboard[i] = checkboard[i][:j]+'Q'+checkboard[i][j+1:]
# 删除皇后
checkboard[i] = checkboard[i][:j]+'.'+checkboard[i][j+1:]
  1. 注意初始棋盘格的创建方式
checkboard = ['.'*n for _ in range(self.n)]

实际上利用了 'a'*3=='aaa',类似[0]*3==[0,0,0]