❝有多少次,刷到N皇后,看到标注难度hard,连题目看都不想看,就直接跳过。但是试着做了一次,一把ac,原来简单到爆。
本篇只有一道N皇后题目,目的为提醒自己,要敢于尝试,敢于思考。
N皇后题目在leetCode有两道
- 51.N皇后
- 52.N皇后II
52相比51只不过是不用输出具体N皇后棋盘摆放结果,输出个数即可。
自然输出题解不是重点,就以52题练习为主。
52.N皇后II
题目描述:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为8皇后的一种解法。
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
示例:
输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
提示:
皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一或 N-1 步,可进可退。(引用自 百度百科 - 皇后 )
解题思路:
- O(n)空间复杂度解题
棋盘摆放皇后的规则:在同一行,同一列,左对角线,右对角线都不能有别的皇后。
N皇后,是典型的回溯问题,剪枝条件按照皇后摆放规则考虑即可。
从每一行每一列开始,摆放棋子,在搜索到每一列的最后一行时,解过+1,然后进行回退。
如何剪枝,校验皇后的合法性,需要维护变量,来表示行列,左上对角线,右上对角线的皇后摆放情况。
最直观的思路就是维护n*n的二维数组。不过,有个更省空间且判断更高效简洁的思路。
对于行列的判断,只需要维护n大小的数组,表示第n行的皇后放在哪一列即可。
对于坐上对角线,和右上对角线,有个规律:
左上对角线的 row - col 都是相等的
右上对角线的 row + col 都是相等的
这样对于左上对角线和右上对角线的皇后摆放情况,就可以维护一个最大2*n
大小的一维数组来表示。
class Solution {
public int res = 0;
public int totalNQueens(int n) {
boolean[] colRes = new boolean[n];
boolean[] leftupRes = new boolean[2 * n], rightupRes = new boolean[2 * n];
dfs(colRes, leftupRes, rightupRes, n, 0);
return res;
}
private void dfs(boolean[] colRes, boolean[] leftupRes, boolean[] rightupRes, int n, int row) {
if (row == n) {
res++;
return;
}
for (int col = 0; col if (colRes[col] || leftupRes[n + row - col] || rightupRes[row + col]) {
continue;
}
colRes[col] = true;
leftupRes[n + row - col] = true;
rightupRes[row + col] = true;
dfs(colRes, leftupRes, rightupRes, n, row + 1);
colRes[col] = false;
leftupRes[n + row - col] = false;
rightupRes[row + col] = false;
}
}
}
如果熟悉了回溯写法,可以很快写出这段代码。
因为基于朴素回归,所以时间复杂度也是指数级别的,为O(n!),空间复杂度O(n)。
- O(1)空间,位运算优化
做到这一步,已经可以了,但是空间复杂度还可以进一步优化,利用位运算,降到O(1)。
代替一维数组,分别用int变量的二进制的每一位来表示 colRes
, leftupRes
, rightupRes
在棋盘中的位置。
操作比较花哨也巧妙,具体思路解法可参考官方题解。
https://leetcode-cn.com/problems/n-queens-ii/solution/nhuang-hou-ii-by-leetcode-solution/