在问 · 算法题解

51. N 皇后

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

题目描述

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

找出所有满足要求的摆法并返回。每种摆法用一个字符串数组表示,数组第 i 项对应棋盘第 i 行,字符 'Q' 表示皇后,'.' 表示空格。

各种摆法在结果中的先后顺序不限。

示例 1
输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]

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

示例 2
输入:n = 1
输出:[["Q"]]

解释:1 x 1 棋盘只能放一个皇后。

数据范围

  • 1 <= n <= 9

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

按行放置并用集合剪枝。

为什么正确

三个集合排除列与两条对角线冲突。

复杂度分析

时间 O(n!),额外空间 O(n)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

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

class Solution:
 def solveNQueens(self,n):
  out=[];b=[['.']*n for _ in range(n)];C=set();D=set();E=set()
  def f(r):
   if r==n:out.append([''.join(x) for x in b]);return
   for c in range(n):
    if c in C or r-c in D or r+c in E:continue
    C.add(c);D.add(r-c);E.add(r+c);b[r][c]='Q';f(r+1);b[r][c]='.';C.remove(c);D.remove(r-c);E.remove(r+c)
  f(0);return out

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

其他解法

两种方法都是逐行回溯;集合版本直观,位运算版本把冲突判断和候选枚举压缩成几条整数运算,速度更快。

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

用三个整数 cols、d1、d2 的二进制位记录被列和两条对角线占用的位置。每行可放的位置是 full & ~(cols | d1 | d2)。用 avail & -avail 依次取出最低位的 1 放皇后。进入下一行时,d1 左移一位,d2 右移一位,表示对角线向下延伸。

复杂度和集合版本相同,但位运算代替哈希查找,常数小很多。可用位置直接枚举,不用逐列试探。代码需要理解移位和取最低位的技巧。

class Solution:
    def solveNQueens(self, n):
        out = []
        queens = [0] * n
        full = (1 << n) - 1

        def dfs(r, cols, d1, d2):
            if r == n:
                out.append(['.' * c + 'Q' + '.' * (n - c - 1) for c in queens])
                return
            avail = full & ~(cols | d1 | d2)
            while avail:
                bit = avail & -avail
                avail &= avail - 1
                queens[r] = bit.bit_length() - 1
                dfs(r + 1, cols | bit, ((d1 | bit) << 1) & full, (d2 | bit) >> 1)

        dfs(0, 0, 0, 0)
        return out

回溯 专题的其他题目

查看 回溯 全部题目 →