题目描述:
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
来源:力扣(LeetCode)
思路:
1)首先需要一个 判断位置是否合法的方法!
①不仅要判断相邻位置,还要判断之前的所有位置;需要一个记录之前每层摆放位置的List;
②近似看作两个位置不能组成一个正方形,判断的是 对角线!
boolean isTrue(int index){
//不符合规定的三种情况
int num = level.size();//得到的是,现在要摆放的层数!
for(int i = 0;i < num;i++){
int a = level.get(i);
if(index == a || (index - a == num - i) || (index + num - i == a)) return false;
}
return true;
}
2)回溯的内容:path列表、记录层数的level列表、每次设置的StringBuilder 三个内容!
if(isTrue(i)){
str.setCharAt(i,'Q');
path.add(new String(str));
level.add(i);
backTrack(n,level);
level.remove(level.size() - 1);
path.remove(path.size() - 1);
str.setCharAt(i,'.');//回溯
}
3)剪枝优化:还不清楚!
字符数组模拟棋盘实现:
1)判断条件使用简单的数组位置上的属性进行判断;
2)声明一个将二维数组添加到结果集的方法;
3)使用了棋盘,回溯里的逻辑就简单了许多!
判断时,下标 0 n之类的只要前后一直,保持不变即可!
代码:
1)手写回溯 + 各种判断条件
class Solution {
List<List<String>> res = new ArrayList<>();//记录最后的结果!
List<String> path = new ArrayList<>();//记录每次的路径
StringBuilder stb = new StringBuilder();
List<Integer> level = new ArrayList<>();//记录层数和摆放的位置
public List<List<String>> solveNQueens(int n) {
for(int i = 0;i < n;i++){
stb.append('.');
}//确定了一个全是 "."的字符串,每层只要修改就好
//第一行的每个位置都可以尝试,所以 传入的为 -2
backTrack(n,level);
return res;
}
//结果集res中存储的List是n个String,一个String表示一层
//要传入层数,和第几层在哪个位置上!
//用数组记录!
void backTrack(int n,List<Integer> level){
//存够了 n 个层,就说明满足了结果
if(path.size() == n){
res.add(new ArrayList(path));
return;
}
//是每一层都从头开始的回溯方式
//难点在于 不符合摆放的条件判断
StringBuilder str = new StringBuilder(stb);
for(int i = 0;i < n;i++){
//情况合适,就进入循环
if(isTrue(i)){
str.setCharAt(i,'Q');
path.add(new String(str));
level.add(i);
backTrack(n,level);
level.remove(level.size() - 1);
path.remove(path.size() - 1);
str.setCharAt(i,'.');//回溯
}
continue;
}
}
//判断位置是否合法的情况
//不能只判断相邻的 两层!
//和之前的每一个点都不能在同一个正方形上
boolean isTrue(int index){
//不符合规定的三种情况
int num = level.size();//得到的是,现在要摆放的层数!
for(int i = 0;i < num;i++){
int a = level.get(i);
if(index == a || (index - a == num - i) || (index + num - i == a)) return false;
}
return true;
}
}
2)利用二维字符数组实现:
class Solution {
List<List<String>> res = new ArrayList<>();//记录最后的结果!
public List<List<String>> solveNQueens(int n) {
//使用数组模拟一个棋盘
char[][] chessboard = new char[n][n];
//生成一个 . 的棋盘
for (char[] c : chessboard) {
Arrays.fill(c, '.');
}
backTrack(n,0,chessboard);//把总共可以尝试的 列传进去n,第几行row用来判断位置;chessboard用来更改棋盘
return res;
}
void backTrack(int n,int row,char[][] chessboard){
if(row == n){
res.add(addPath(chessboard));
return;
}
for(int i = 0;i < n;i++){
//单层逻辑
if(isTrue(row,i,n,chessboard)){
chessboard[row][i] = 'Q';
backTrack(n,row + 1,chessboard);
chessboard[row][i] = '.';
}
}
}
//棋盘改变之后还要添加
List<String> addPath(char[][] chessboard){
List<String> path = new ArrayList<>();
for(char[] ch : chessboard){//每次拿到的是一个char数组
path.add(new String(ch));
}
return path;
}
//判断位置合法性
boolean isTrue(int row,int col,int n,char[][] chessboard){
//三种情况
for(int i = 0;i < row;i++){//判断列
if(chessboard[i][col] == 'Q') return false;
}
//对角线45°
for(int i = row - 1,j = col - 1;i >=0 && j >= 0;i--,j--){
if(chessboard[i][j] == 'Q') return false;
}//左边135°对角线
for(int i = row - 1,j = col + 1;i >= 0 && j <= n - 1;i--,j++){
if(chessboard[i][j] == 'Q') return false;
}
return true;
}
}