在问 · 算法题解

52. N 皇后 II

困难回溯回溯 + 集合剪枝力扣原题 ↗

题目描述

皇后能攻击同一行、同一列以及两条对角线上的任意棋子。输入整数 n,在 n x n 的棋盘上摆 n 个皇后,要求任意两个皇后都互不攻击。

返回满足要求的不同摆法的总数。

示例 1
输入:n = 4
输出:2

解释:4 x 4 棋盘上只有 2 种合法摆法。

示例 2
输入:n = 1
输出:1

数据范围

  • 1 <= n <= 9

解题思路:回溯 + 集合剪枝

逐行放置:第 r 行只放一个皇后,枚举它的列 c。用三个集合记录已占用的列、主对角线(r-c 相同)、副对角线(r+c 相同)。如果 (r,c) 不在任何一个集合中,就放下并递归到下一行,回来后撤销。放完第 n 行说明找到 1 种方案,把子问题的方案数加起来即可。

为什么正确

进入 dfs(r) 时,前 r 行各有一个皇后且互不攻击;三个集合恰好对应这 r 个皇后。

复杂度分析

时间约 O(n!),空间 O(n)。

易错点

斜线要分两个方向:r-c 和 r+c。只记一个会把另一方向的冲突漏掉;r-c 可能为负,用数组时要加 n-1 偏移。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def totalNQueens(self, n: int) -> int:
        cols, diag1, diag2 = set(), set(), set()

        def dfs(r):
            if r == n:
                return 1
            count = 0
            for c in range(n):
                if c in cols or r - c in diag1 or r + c in diag2:
                    continue
                cols.add(c)
                diag1.add(r - c)
                diag2.add(r + c)
                count += dfs(r + 1)
                cols.remove(c)
                diag1.remove(r - c)
                diag2.remove(r + c)
            return count

        return dfs(0)

载入编辑器,自己跑一遍 →

其他解法

两者搜索树相同;集合版本便于理解,位运算版本省去哈希开销,是本题最快的写法。

解法 2:回溯 + 位运算 时间 O(n!),空间 O(n)

用整数 cols、d1、d2 的二进制位分别记录被占用的列和两条对角线。当前行的候选位置是 full & ~(cols | d1 | d2)。每次用 avail & -avail 取出最低位放皇后,递归到下一行时 d1 左移、d2 右移。cols 等于 full 时说明 n 行都放好了,计数加一。

只需要计数,不用记录棋盘,所以位运算版本的优势更明显。冲突判断是 O(1) 的整数运算,常数远小于集合。

class Solution:
    def totalNQueens(self, n: int) -> int:
        full = (1 << n) - 1

        def dfs(cols, d1, d2):
            if cols == full:
                return 1
            count = 0
            avail = full & ~(cols | d1 | d2)
            while avail:
                bit = avail & -avail
                avail &= avail - 1
                count += dfs(cols | bit, ((d1 | bit) << 1) & full, (d2 | bit) >> 1)
            return count

        return dfs(0, 0, 0)

回溯 专题的其他题目

查看 回溯 全部题目 →