题目链接:https://leetcode-cn.com/problems/n-queens/
每日一题碰到 N皇后II 了,先做一下 N皇后。
主要思路就是递归加回溯了,真·暴力解决也很简单,直接一直递归最后判断是否满足 N皇后 的条件即可。
这里考虑尽可能的剪枝来降低时间消耗,首先题目给出 皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上
。所以可以用这个条件来判断满足进入递归的条件,开始我的思路是每次填充一个皇后时,将她的横行、纵行、斜线行和反斜线行设置标记,下一层填充皇后时判断当前位置是否可以放置,可以就进入下一层递归,不能就判断下一个位置。这样一直递归到最后一行时,如果第 n-1 行填充了,说明这个解法可行,添加到 答案 中。这里因为一行一行的递归,所以横行的判断是多余的
。
但是这样每次填充一个皇后之后,设置标记会浪费很多时间,所以优化了一下。这里选择分别用一个 列数组、斜线数组 和 反斜线数组 来记录当前是否可以填充皇后。
这里用 boolean[] column
来存储列的情况,用 boolean[] slash
来存储斜行的情况,用 boolean[] backslash
来存储反斜行的情况。两边的矩阵横轴上的是列,纵轴上的是行,可以看到左边直接用 行数 + 列数
得到的数字正好是对应的斜行。右边需要对列进行处理,可以看到是用绿色的坐标就正好是对应的反斜行数,这里正好可以用 n-1-列数
来代替 列数
。这样判断到一个 皇后 填充的位置时,可以得到他的 列数、斜行数 和 反斜行数。假设 皇后 填充位置为 [ i , j ]
,那么可以得到她的 列数 = j
, 斜行数 = i + j
,斜行数 = n - 1 - j + i
。然后判断 column[列数]
、 slash[斜行数]
、 backslash[反斜行数]
是否都为 false ,如果是则可填充,否则判断下一个。
步骤类似下图:
每行会遍历所有列,如果某个位置满足列、斜行、反斜行的要求,那么加入该点进行下一行的递归,如果不满足就直接查看该行的下一个位置。
以上总结为代码如下:
package com.company;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class lc51 {
List<List<String>> ans = new LinkedList<>();
public List<List<String>> solveNQueens(int n) {
if(n==0)return ans;
char[][] chars= new char[n][n];
for(char[] ch : chars){
Arrays.fill(ch,'.');
}
boolean[] column = new boolean[n];
boolean[] slash = new boolean[2*n-1];
boolean[] backslash = new boolean[2*n-1];
find(chars,column,slash,backslash,0);
return ans;
}
public void find(char[][] chars,boolean[] column,boolean[] slash,boolean[] backslash,int i){
if(i==chars.length){
List<String> list = new LinkedList<>();
for(char[] ch : chars){
list.add(String.valueOf(ch));
}
ans.add(list);
}else{
for (int j = 0; j < chars.length; j++) {
int slashTemp = i+j;
int backslashTemp = chars.length-1-j+i;
if(!column[j]&&!slash[slashTemp]&&!backslash[backslashTemp]){
chars[i][j] = 'Q';
column[j] = true;
slash[slashTemp] = true;
backslash[backslashTemp] = true;
find(chars,column,slash,backslash,i+1);
chars[i][j] = '.';
column[j] = false;
slash[slashTemp] = false;
backslash[backslashTemp] = false;
}
}
}
}
}
最终顺利通过