题目描述
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
解题思路
不能互相攻击图解(打X的地方不能再放置皇后)
看图理解,简化后的验证条件:按行遍历,第N行的皇后:
- 同一列不能有皇后
- 左上方不能有皇后
- 右上方不能有皇后
问题简化
下面我们将八皇后问题转化为四皇后问题,并用回溯法来找到它的解
目的:在4x4棋盘上,使得4个皇后不能在同行同列以及同斜线上。
step1
尝试先放置第一枚皇后,被涂黑的地方是不能放皇后
step2
第二行的皇后只能放在第三格或第四格,比方我们放第三格,则:
此时我们也能理解为什么叫皇后问题了,皇后旁边容不下其他皇后。而在同一个房间放下四个皇后确实是个不容易的问题。
step3
可以看到再难以放下第三个皇后,此时我们就要用到回溯算法了。我们把第二个皇后更改位置,此时我们能放下第三枚皇后了。
step4
虽然是能放置第三个皇后,但是第四个皇后又无路可走了。返回上层调用(3号皇后),而3号也别无可去,继续回溯上层调用(2号),2号已然无路可去,继续回溯上层(1号),于是1号皇后改变位置如下,继续回溯。
这就是回溯算法的精髓,就是根据这个算法,最终能够把四位皇后放在4x4的棋盘里。也能用同样的方法解决了八皇后问题。下面我们用代码解决八皇后问题。
使用回溯法实现,可以套用模板,参见
使用回溯算法分割回文串
代码实现
package com.lingluo;
/**
* @author 灵洛
* @date 23:05 2020/3/24
*/
import java.util.ArrayList;
import java.util.List;
/**
* @author
* @date 2020/3/24 23:05
*/
public class N_Queens_51 {
public List<List<String>> solveNQueens(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] = '.';
}
}
List<List<String>> results = new ArrayList<List<String>>();
backTrack(board, 0, n, results);
return results;
}
private void backTrack(char[][] board, int row, int n, List<List<String>> results) {
if (row == n) {
// 这条路走完了,记录结果
results.add(toList(board));
}
for (int col = 0; col < board[0].length; col++) {
//枚举所有的选择
if (!checkValidPath(board, row, col)) {
// 路径冲突,剪枝
continue;
}
// 路径不冲突,选择
board[row][col] = 'Q';
// 继续进行递归,row+1
backTrack(board, row+1, n, results);
// 撤销选择,回溯
board[row][col] = '.';
}
}
/**
* 检查皇后之间是否不能互相攻击
* @param board 当前棋盘情况
* @param row 行
* @param col 列
* @return 满足返回true
*/
private boolean checkValidPath(char[][] board, int row, int col) {
int n = board[0].length;
// 同一列不能有皇后
for (int i = 0; i < n; 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;
}
/**
* char数组转成List
* @param board
* @return
*/
private List<String> toList(char[][] board) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < board[0].length; i++) {
list.add(new String(board[i]));
}
return list;
}
}