1. 概述
「N 皇后问题」研究的是如何将 N个皇后放置在 N × N 的棋盘上,并且使皇后彼此之间不能相互攻击。
皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。
以 leetcode51 为例讲解N皇后问题 https://leetcode-cn.com/problems/n-queens/
1.1 图解
简单来说,就是保证横向,纵向以及对角线不存在重复的皇后即可。那为了快速判断该点是否可以放置皇后,我们用三个集合分别记录:
- 记录某个列是否存在皇后 boolean[] cols
这个比较好理解,比如第一列已经存在了皇后,则cols[0] = true
- 记录某个左对角线是否存在皇后 Map<Integer, Boolean> diaLeft
左对角线有一个规律:同一左对角线,x轴y轴坐标相加值相等
- 记录某个左对角线是否存在皇后 Map<Integer, Boolean> diaRight
右对角线同样有个规律:同一右对角线,x轴y轴坐标相减的值相等
1.2 实现
boolean[] cols; // 标记某个列是否存在皇后
Map < Integer, Boolean > diaLeft; // 左对角线是否存在皇后
Map < Integer, Boolean > diaRight; // 右对角线是否存在皇后
public List < List < String >> solveNQueens(int n) {
List < List < String >> res = new ArrayList < > ();
if(n == 0) return res;
List < Integer > deque = new LinkedList < > ();
cols = new boolean[n];
diaLeft = new HashMap < > (2 * n - 1);
diaRight = new HashMap < > (2 * n - 1);
dfs(n, 0, deque, res);
return res;
}
private void dfs(int n, int index, List < Integer > deque, List < List < String >> res) {
if(index == n) {
res.add(buildBoard(deque));
return;
}
for(int i = 0; i < n; i++) {
if(!cols[i]
&& !diaLeft.getOrDefault(index + i, false)
&& !diaRight.getOrDefault(index - i, false)) {
deque.add(i);
cols[i] = true;
diaLeft.put(index + i, true);
diaRight.put(index - i, true);
dfs(n, index + 1, deque, res);
deque.remove(deque.size() - 1);
cols[i] = false;
diaLeft.put(index + i, false);
diaRight.put(index - i, false);
}
}
}
private List < String > buildBoard(List < Integer > deque) {
int n = deque.size();
List < String > board = new ArrayList < String > ();
for(int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[deque.get(i)] = 'Q';
board.add(new String(row));
}
return board;
}