题目:力扣https://leetcode-cn.com/problems/n-queens/
class Solution {
public List<List<String>> solveNQueens(int n) {
Set<Integer> diagonal1 = new HashSet<Integer>();
Set<Integer> diagonal2 = new HashSet<Integer>();
Set<Integer> columns = new HashSet<Integer>();
List<List<String>> ans = new ArrayList<List<String>>();
int[] map = new int[n];
Arrays.fill(map,-1);
putQueen(ans,n,map,0,columns,diagonal1,diagonal2);
return ans;
}
private void putQueen(List<List<String>> ans,int n,int[] map,int row,Set<Integer> columns,Set<Integer> diagonal1,Set<Integer> diagonal2){
//出口
if(row==n){
List<String> temp = pack(map,n);
ans.add(temp);
}else{
//判断
for(int i=0;i<n;i++){
if(columns.contains(i)){
continue;
}
int dia1 = row-i;
if(diagonal1.contains(dia1)){
continue;
}
int dia2 = row+i;
if(diagonal2.contains(dia2)){
continue;
}
//记录
map[row] = i;
columns.add(i);
diagonal1.add(dia1);
diagonal2.add(dia2);
putQueen(ans,n,map,row+1,columns,diagonal1,diagonal2);
//状态重置
map[row] = -1;
columns.remove(i);
diagonal1.remove(dia1);
diagonal2.remove(dia2);
}
}
}
//转换格式
private List<String> pack(int[] map,int n){
List<String> res = new ArrayList<String>();
for(int i=0;i<n;i++){
StringBuilder temp = new StringBuilder();
for(int j=0;j<n;j++){
if(j==map[i]){
temp.append("Q");
}else{
temp.append(".");
}
}
res.add(temp.toString());
}
return res;
}
}
(补充:皇后的攻击范围是:皇后所在位置对应的行、列和两条对角线。因此,放置了一个皇后之后,其所在的行、列以及两道对角线则不能再放置下一个皇后。)
思路:但凡遇到需要用到递归方法的题目,现在的我还是不能独立完成,每次都需要研究一遍题解才能自己写出来。 这题类似于leetcode36.有效的数独,相比之下,我感觉这题稍稍简单于leetcode36。这题的递归比较清晰明了,每一行就是一层递归,每到新的一层遍历该层所有的格子,通过columns、diagonal1和diagonal2三个HashSet来判断当前格子是否可以放置新的皇后。若不可以,则continue继续遍历下一个格子;若可以则修改columns、diagonal1和diagonal2三个HashSet的状态以便后续判断。因为每一层只能放一个皇后,所以每放置完一个皇后就需要继续“递”到下一层去。若某一层所有格子都无法放置皇后,则表示当前的解不成立,需要回溯,将columns、diagonal1和diagonal2三个HashSet的状态重置(即将刚才记录的东西撤销,避免影响后续的判断),回溯到上一层遍历下一个格子。若出现row==n,即表示每一层都有皇后,并且已经放置到了最后一层,意味着当前解成立。此时,我们需要通过pack()方法,将map记录下来的解转换成List<String>的格式,然后将上述List装入List<List<String>>类型的ans中,执行完递归方法后,返回ans。
1.在主方法中声明后续需要用到的各个变量。diagonal1和diagonal2是表示当前格子对应的两道对角线,columns表示当前格子的所在列,ans是最后需要返回的参数,map是用于记录每一行皇后所在的位置(因为刚开始没有皇后在棋盘上,所以把所有的值默认设置为-1)。
Set<Integer> diagonal1 = new HashSet<Integer>();
Set<Integer> diagonal2 = new HashSet<Integer>();
Set<Integer> columns = new HashSet<Integer>();
List<List<String>> ans = new ArrayList<List<String>>();
int[] map = new int[n];
Arrays.fill(map,-1);
2.简单写一个转换格式的方法,用于最后得出解后将解转换成题目要求的格式。map[]是记录每一行的皇后位于那个格子,n是皇后的数量。
//转换格式
private List<String> pack(int[] map,int n){
List<String> res = new ArrayList<String>();
for(int i=0;i<n;i++){
StringBuilder temp = new StringBuilder();
for(int j=0;j<n;j++){
if(j==map[i]){
temp.append("Q");
}else{
temp.append(".");
}
}
res.add(temp.toString());
}
return res;
}
3.写一个递归的方法。入参7个,列表ans,皇后数量n,皇后位置图map,当前所在的行数row(默认值为0),记录列上是否存在皇后的columns,记录对角线上是否存在皇后的diagonal1和diagonal2。
private void putQueen(List<List<String>> ans,int n,int[] map,int row,Set<Integer> columns,Set<Integer> diagonal1,Set<Integer> diagonal2){
//......
}
4.定义递归出口。若row==n,则以为值当前解可以成立,调用pack()方法将答案转换成题目所要求的输出格式,加入到最后需要返回的列表ans中去。
if(row==n){
List<String> temp = pack(map,n);
ans.add(temp);
}
5.判断是否可以放置皇后。如果row尚未到达n值,则表示还有皇后没有放置完。因此需要判断当前位置是否可以放置皇后。通过contains()方法判断当前位置的columns、diagonal1和diagonal2中是否已经存在皇后(其中diagonal1和值用行下标与列下标的差表示,diagonal2的值用行下表和列下标的和表示。)。若已经存在皇后则当前位置不可以放置皇后,continue继续遍历下一个位置;反之,则可以放置皇后。
//判断
for(int i=0;i<n;i++){
if(columns.contains(i)){
continue;
}
int dia1 = row-i;
if(diagonal1.contains(dia1)){
continue;
}
int dia2 = row+i;
if(diagonal2.contains(dia2)){
continue;
}
6.记录当前位置放置皇后之后的相关信息。若经过上述判断,得出当前位置可以放置皇后,则需要进入“记录”的步骤。将当前格子的位置记录在map中,然后规定该列和对应的两道对角线不允许再出现皇后(至于行为什么没有提及,因为每放完一个皇后即刻递到下一层就已经完成了换行操作,所以不需要赘述。)通过putQueen()递到下一层,row值+1。
//记录
map[row] = i;
columns.add(i);
diagonal1.add(dia1);
diagonal2.add(dia2);
putQueen(ans,n,map,row+1,columns,diagonal1,diagonal2);
7.状态重置。若当前解row无法到达n值,则表示当前解不成立,需要回溯到上一层。回溯时需要将刚刚记录的东西撤销掉,即状态恢复。将当前位置在map上的标志改为初始值,将该位置对应的列和两道对角线存储的信息撤销,然后回到上一层继续遍历下一个位置。
//状态重置
map[row] = -1;
columns.remove(i);
diagonal1.remove(dia1);
diagonal2.remove(dia2);
8.在主方法中调用putQueen(),传入相关参数。执行完该递归方法后,返回ans即可。
putQueen(ans,n,map,0,columns,diagonal1,diagonal2);
return ans;