面试知识库

51. N皇后#

今天要挑战的是一道算法界的”皇后级”难题 - LeetCode 51「N皇后」。这道题困扰了无数程序员,但今天我们用象棋玩家的思维,一步步将它拆解成简单易懂的小问题!

🎮 从象棋到算法#

你有没有下过国际象棋?皇后是棋盘上最强大的棋子,她可以横向、纵向、斜向任意方向移动任意步数。N皇后问题就像是在玩一局特殊的象棋:我们需要在N×N的棋盘上放置N个皇后,让她们互相都无法攻击到对方。

想象你在玩一局特殊的象棋,需要一步步地把皇后放在安全的位置上。每放置一个皇后,就会在棋盘上形成一片”危险区域”,下一个皇后就不能放在这些位置上。这不就是我们程序要解决的问题吗?

问题描述#

题目目标#

按照 n 皇后问题的规则,在 n x n 的棋盘上放置 n 个皇后,并返回所有不同的解法。每一种解法包含一个棋盘布局,其中 Q 表示皇后,. 表示空位。

示例 1#

输入: n = 4 输出: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] 棋盘示意:

. Q . .
. . . Q
Q . . .
. . Q .
text

说明: 图示把输入结构和输出结果并排展开后,会更容易直接看清题目的变化过程。

补充说明#

  • 棋盘中的 Q 表示皇后,. 表示空位。

💡 问题本质剖析#

题目要求: 在一个N×N的棋盘上放置N个皇后,使得任意两个皇后都不能互相攻击。返回所有可能的解决方案。

输入输出示例:

输入:n = 4
输出:[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
plaintext

🎯 思路突破#

就像下象棋时,我们会思考:

  1. 每一行必须有一个皇后(因为如果同一行有两个皇后必然会互相攻击)
  2. 每一列必须有一个皇后(同理)
  3. 任意两个皇后不能在同一条斜线上

这启发我们可以:

  • 逐行放置皇后
  • 记录已经被攻击的列和斜线
  • 用回溯法尝试所有可能的放置方案

🚀 代码实现#

📝 解题思路详解#

让我们像下象棋一样,一步步理解这个解法:

1. 棋盘表示#

我们用一维数组 queens 记录每行皇后的列位置,这比二维数组更高效。比如 queens[2] = 3 表示第2行的皇后放在第3列。

2. 攻击区域检查#

为了快速判断一个位置是否安全,我们用三个布尔数组记录被攻击的位置:

  • cols[j] 表示第j列是否有皇后
  • diag1[i+j] 表示主对角线是否有皇后
  • diag2[i-j+n-1] 表示副对角线是否有皇后

这就像象棋中提前计算好皇后的攻击范围!

3. 回溯过程#

就像下象棋时的思考过程:

  • 在当前行找一个安全的位置放皇后
  • 标记这个皇后的攻击范围
  • 转移到下一行继续放置
  • 如果遇到死路,就回溯到上一步重新尝试

🔍 优化技巧#

  1. 判断优化:

    • 使用位运算代替布尔数组,可以进一步优化空间和时间
    • 预先计算每个位置的攻击范围
  2. 空间优化:

    • 只用一维数组记录皇后位置
    • 用整数代替布尔数组记录攻击情况
  3. 回溯优化:

    • 可以利用对称性减少搜索范围
    • 首行皇后只需要搜索一半位置

🎯 相关题目推荐#

  • N皇后 II(LeetCode 52)- 只需要计算解的数量
  • 解数独(LeetCode 37)- 类似的回溯思想
  • 放置盒子(LeetCode 1411)- 类似的约束放置问题

🌟 面试常见追问#

  1. 如何处理大规模问题?

    • 可以使用并行计算
    • 利用问题的对称性减少计算量
  2. 能否用其他算法解决?

    • 可以用约束编程(Constraint Programming)
    • 可以用遗传算法等启发式方法