第51题. N皇后
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
- 输入:n = 4
- 输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
- 解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
- 输入:n = 1
- 输出:[[“Q”]]
思路:
设置标记棋盘数组,0为空位,1为皇后,2为不可下棋位置。即其他皇后攻击范围。
递归返回条件: 皇后数量达到最大值 n
因为棋盘每旋转90度又是一个一模一样的结果,所以可以用哈希去重。
每次下棋时判断这个位置是不是空位,是否在其他皇后攻击范围内,如果满足以上条件,跳过当前位置。否则就在这个位置下棋,当前位置横竖斜,呈米字型辐射本皇后攻击范围,赋值为2。开始递归。如果遇到不满足n皇后情况,回溯。
package com.programmercarl.backtracking;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* @ClassName SolveNQueens
* @Descriotion TODO
* @Author nitaotao
* @Date 2022/7/12 15:48
* @Version 1.0
* https://leetcode.cn/problems/n-queens/
* 51. N 皇后
**/
public class SolveNQueens {
List<List<String>> result = new ArrayList<>();
HashMap<String, List<String>> map = new HashMap<>();
public List<List<String>> solveNQueens(int n) {
//0为空 1为棋 2作为标记 表示不能放置棋子
int[][] arr = new int[n][n];
backtracking(arr, 0, n,0);
for (String key : map.keySet()) {
result.add(map.get(key));
}
System.out.println(result);
return result;
}
/**
* @param arr 棋盘
* @param queueNum 当前皇后出现数量
* @param n 应放置的皇后数量
* @param startX 从之前走过的路接着走
*/
public void backtracking(int[][] arr, int queueNum, int n,int startX) {
//当 n 个皇后全部放置完毕,则返回
if (queueNum == n) {
String all = "";
List<String> list = new ArrayList();
for (int i = 0; i < arr.length; i++) {
String s = "";
for (int j = 0; j < arr.length; j++) {
if (arr[i][j] == 1) {
s += "Q";
} else {
s += ".";
}
}
all += s;
list.add(s);
}
if (!map.containsKey(all)) {
map.put(all, list);
}
return;
}
for (int i = startX; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
//简单判断一下当前位置是否可以下棋
if (arr[i][j] != 0) {
continue;
}
boolean findQueue = false;
//横向纵向不能有 1
for (int k = 0; k < arr.length; k++) {
findQueue = arr[i][k] == 1 || arr[k][j] == 1;
if (findQueue) {
break;
}
}
if (findQueue) {
//如果这些位置有1,则有冲突
continue;
}
//左斜右斜不能有 1
//左上
int x = i - 1;
int y = j - 1;
while (x > -1 && y > -1) {
findQueue = arr[x][y] == 1;
if (findQueue) {
break;
}
x--;
y--;
}
if (findQueue) {
//如果这些位置有1,则有冲突
continue;
}
//左下
x = i - 1;
y = j + 1;
while (x > -1 && y < n) {
findQueue = arr[x][y] == 1;
if (findQueue) {
break;
}
x--;
y++;
}
if (findQueue) {
//如果这些位置有1,则有冲突
continue;
}
//右上
x = i + 1;
y = j - 1;
while (x < n && y > -1) {
findQueue = arr[x][y] == 1;
if (findQueue) {
break;
}
x++;
y--;
}
if (findQueue) {
//如果这些位置有1,则有冲突
continue;
}
//右下
x = i + 1;
y = j + 1;
while (x < n && y < n) {
findQueue = arr[x][y] == 1;
if (findQueue) {
break;
}
x++;
y++;
}
// 如果 findQueue == true ,即当前位置横向、纵向或斜向出现 1 ,
// 即出现了其他皇后,跳过
if (findQueue) {
//如果这些位置有1,则有冲突
continue;
}
if (arr[i][j] == 0) {
//若为0,才能添加棋子,否则跳过
//此元素 横、纵、左斜、右斜元素标记为2,不可再写
for (int k = 0; k < arr.length; k++) {
arr[i][k] = 2;
arr[k][j] = 2;
}
//左上
x = i - 1;
y = j - 1;
while (x > -1 && y > -1) {
arr[x][y] = 2;
x--;
y--;
}
//左下
x = i - 1;
y = j + 1;
while (x > -1 && y < n) {
arr[x][y] = 2;
x--;
y++;
}
//右上
x = i + 1;
y = j - 1;
while (x < n && y > -1) {
arr[x][y] = 2;
x++;
y--;
}
//右下
x = i + 1;
y = j + 1;
while (x < n && y < n) {
arr[x][y] = 2;
x++;
y++;
}
// 在此处下棋
arr[i][j] = 1;
queueNum++;
//递归
backtracking(arr, queueNum, n, i + 1);
//回溯
queueNum--;
for (int k = 0; k < arr.length; k++) {
arr[i][k] = 0;
arr[k][j] = 0;
}
//左上
x = i - 1;
y = j - 1;
while (x > -1 && y > -1) {
arr[x][y] = 0;
x--;
y--;
}
//左下
x = i - 1;
y = j + 1;
while (x > -1 && y < n) {
arr[x][y] = 0;
x--;
y++;
}
//右上
x = i + 1;
y = j - 1;
while (x < n && y > -1) {
arr[x][y] = 0;
x++;
y--;
}
//右下
x = i + 1;
y = j + 1;
while (x < n && y < n) {
arr[x][y] = 0;
x++;
y++;
}
}
}
}
}
public static void main(String[] args) {
new SolveNQueens().solveNQueens(4);
}
}