跳转至

N皇后

大名鼎鼎的难题

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

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

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

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

例如4皇后:

  • 输入:n = 4
  • 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
  • 解释:如上图所示,4 皇后问题存在两个不同的解法。

回溯算法

其实回溯就是略加优化的暴力搜索,会在不符合条件的情况下进行剪枝。

代码

用三个集合来记录已经放置了皇后的列、主对角线、副对角线,然后对每一行回溯搜索

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:

        def gen_board(queens):
            """格式化输出"""
            board = ["." * n] * n
            for i, q in enumerate(queens):
                board[i] = board[i][:q] + "Q" + board[i][q + 1 :]
            return board

        def backtrack(row):
            """回溯搜索"""
            nonlocal columns, diag1, diag2, queens, sol # 外部变量
            # 递归出口
            if row == n:
                board = gen_board(queens)
                sol.append(board)
            else:
                for i in range(n):
                    # 不满足条件的情况下就跳过这个位置
                    if i in columns or row - i in diag1 or row + i in diag2:
                        continue

                    # 记录答案
                    queens[row] = i

                    # 记录已经被占据的地方
                    columns.add(i)
                    diag1.add(row - i)
                    diag2.add(row + i)

                    # 压栈
                    backtrack(row + 1)

                    # 出栈
                    columns.remove(i)
                    diag1.remove(row - i)
                    diag2.remove(row + i)

        columns = set()
        diag1 = set()
        diag2 = set()
        sol = []
        queens = [-1] * n
        backtrack(0)
        return sol

时间复杂度:$\mathcal{O}(N!)$,其中 N 是皇后数量。

空间复杂度:$\mathcal{O}(N)$,其中 N 是皇后数量。空间复杂度主要取决于递归调用层数、记录每行放置的皇后的列下标的数组以及三个集合,递归调用层数不会超过 N,数组的长度为 N,每个集合的元素个数都不会超过 N。

图解

通过可视化运行,可以清楚看到递归堆栈的过程:


最后更新: 2025-04-07 14:46:13
创建日期: 2024-12-27 23:17:42

评论