首页 简历|笔试面试

52.N皇后II

  • 25年9月4日 发布
  • 902.81KB 共9页
52.N皇后II52.N皇后II52.N皇后II52.N皇后II52.N皇后II

52. N 皇后 II

鲁迅曾说过,有过 N 皇后的经验后,这道题就是纯粹的送分题了,因为题目的

核心解法是完全一样的,只不过这道题目只需要返回方案的个数而已。上一道题

要求返回的是具体的方案,换句话说,只需要在上一道题目的基础上稍作修改即

可。

题意

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

互攻击。

可以参考上一题:051.N 皇后

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

难度

困难

示例

示例 1:

052.N 皇后 II-20241108085456.png

输入:n = 4

输出:2

解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1

输出:1

分析 1

在 051.N 皇后 中,我们已经将递归+回溯讲得很清楚了,如果理解了 051. N 皇后,这道

题目只能算是一个弱化版本,因为我们已经把方案求解出来了,这道题只需要返回方案的

个数即可。

只需要简单改几个地方,先来看题解。

class Solution {

// 记录解的数量

int solutions = 0;

// 解决 N 皇后问题

public int totalNQueens(int n) {

// 初始化棋盘

char[][] board = new char[n][n];

for (int i = 0; i < n; i++) {

for (int j = 0; j < n; j++) {

board[i][j] = '.';

}

}

// 开始递归查找所有解

backtrack(board, 0, n);

return solutions;

}

// 回溯方法

private void backtrack(char[][] board, int row, int n) {

// 如果已成功放置完所有皇后,将当前棋盘方案加入 solutions

if (row == n) {

solutions = solutions + 1;

return;

}

// 尝试在当前行的每一列放置皇后

for (int col = 0; col < n; col++) {

// 检查当前位置是否安全

if (isValid(board, row, col, n)) {

// 放置皇后

board[row][col] = 'Q';

// 递归处理下一行

backtrack(board, row + 1, n);

// 回溯,撤销当前放置

board[row][col] = '.';

}

}

}

// 检查在 (row, col) 位置放置皇后是否安全

private boolean isValid(char[][] board, int row, int col, int n) {

// 检查列上是否有皇后

for (int i = 0; i < row; i++) {

if (board[i][col] == 'Q') {

return false;

}

}

// 检查左上方对角线上是否有皇后

for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {

if (board[i][j] == 'Q') {

return false;

}

}

// 检查右上方对角线上是否有皇后

for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {

if (board[i][j] == 'Q') {

return false;

}

}

return true;

}

}

第一处改动,将 solutions 作为全局变量,用于记录解的数量:

int solutions = 0;

第二处改动,backtrack 回溯方法在找到解的时候将 solutions 加 1 即可:

if (row == n) {

solutions = solutions + 1;

return;

}

第三处改动,删掉 construct 方法,不需要再将当前棋盘状态转换为字符串列表。

回溯和递归的核心框架是不变的,来看题解效率:

052.N 皇后 II-20241108091309.png

分析 2

当然我们还可以稍微做一些变动,通过三个一维数组来记录列、主对角线和副对角线的占

用情况:

• cols:记录哪些列上已有皇后。

• diag1:记录主对角线的状态,主对角线的索引为 row - col + n - 1。

• diag2:记录副对角线的状态,副对角线的索引为 row + col。

052.N 皇后 II-20241108091900.png

①、主对角线:从左上到右下方向,例如 (0,0) -> (1,1) -> (2,2) -> …。在同一主对角线上

的位置 (row, col) 满足 row - col 相等。

也就是说:

• (0, 0): row - col = 0

• (1, 1): row - col = 0

• (2, 2): row - col = 0

主对角线上的元素都满足 row - col = 常数。所以,如果两个位置 (row1, col1) 和 (row2,

col2) 满足 row1 - col1 == row2 - col2,它们就在同一个主对角线上。

对于一个 n x n 的棋盘,row - col 的最小值是 -(n - 1),最大值是 n - 1。

为了便于将这些值转换成数组索引(0 到 2n - 2),我们可以加上 n - 1 的偏移量。因此:

主对角线的数组索引:row - col + (n - 1)

再比如说:

• (0, 1): row - col = -1

• (1, 2): row - col = -1

• (2, 3): row - col = -1

它们仨都在同一主对角线上,负数没办法做索引,所以我们加上偏移量。

052.N 皇后 II-20241108094800.png

②、副对角线:从右上到左下方向,例如 (0,3) -> (1,2) -> (2,1) -> …。在同一副对角线上

的位置 (row, col) 满足 row + col 相等。

比如说:

• (0, 1): row + col = 1

• (1, 0): row + col = 1

它们俩处在同一副对角线上。

052.N 皇后 II-20241108095602.png

也就是说,当我们放一个皇后的时候,对其所在的列、两条对角线分别进行标记,看看是

否满足不能互相攻击的条件。

来看题解:

class Solution05202 {

// 返回 N 皇后问题的解的数量

public int totalNQueens(int n) {

// 初始化列、主对角线、副对角线的布尔数组

boolean[] cols = new boolean[n]; // 列

boolean[] diag1 = new boolean[2 * n - 1]; // 主对角线

boolean[] diag2 = new boolean[2 * n - 1]; // 副对角线

// 使用递归回溯来计数

return backtrack(0, n, cols, diag1, diag2);

}

// 回溯,返回从当前行开始的解的数量

private int backtrack(int row, int n, boolean[] cols, boolean[] diag1, boolean[]

diag2) {

// 如果所有行都放置完毕,说明找到一种有效解,返回 1

if (row == n) {

return 1;

}

int solutions = 0; // 记录解的数量

// 遍历当前行的每一列,尝试放置皇后

for (int col = 0; col < n; col++) {

// 计算当前列、主对角线、副对角线的索引

int d1 = row - col + n - 1; // 主对角线索引

int d2 = row + col; // 副对角线索引

// 检查当前位置是否被占用

if (cols[col] || diag1[d1] || diag2[d2]) {

continue; // 如果当前列或对角线被占用,跳过该位置

}

// 放置皇后,标记当前列和对角线已被占用

cols[col] = true;

diag1[d1] = true;

diag2[d2] = true;

// 递归处理下一行,并累加解的数量

solutions += backtrack(row + 1, n, cols, diag1, diag2);

// 回溯,撤销当前皇后位置的占用状态

cols[col] = false;

diag1[d1] = false;

diag2[d2] = false;

}

return solutions; // 返回当前分支的解的数量

}

public static void main(String[] args) {

Solution05202 solution = new Solution05202();

System.out.println(solution.totalNQueens(4)); // 输出: 2

}

}

效率还不错,beat 100%:

052.N 皇后 II-20241108095504.png

总结

LeetCode 上面有很多系列的题目都是同一个套路,也就是说只要你会了其中一个,就可

以以不变应万变。重点是,你能够把其中的一道题目吃“透”,然后以这道题目为突破点,

把整个系列的题目都“举一反三”掉。

力扣链接:https://leetcode.cn/problems/n-queens-ii/

开通会员 本次下载免费

所有资料全部免费下载! 推荐用户付费下载获取返佣积分! 积分可以兑换商品!
一键复制 下载文档 联系客服