N皇后问题是一个典型的回溯问题。
遇到了回溯问题,根据解题思维框架,我们需要明确几个点:
- 路径
- 选择
- 结束条件
回溯的本质就是对多叉树的遍历,变形点就在于树的前序遍历做选择,后序遍历撤销选择。
那么N皇后问题的关键点是什么:
- 路径:每行选择放置皇后的列
- 选择:每行的全部列都是一个选择
- 结束条件:尝试在全部的行都放置了皇后,即,row > n-1, row起始于0。
N皇后问题还需要注意的一点就是游戏规则的限定,它要求了皇后不能相互攻击,即,放置的皇后在其平面的正八个方向上不能存在皇后,这里对角线上的情况可以通过一个技巧解决:斜率,但我们不进行除法运算,以免发生类型转换和精度丢失。直接比较deltaX和deltaY是否相等或为相反数即可。
leetcode地址:https://leetcode-cn.com/problems/n-queens/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class NQueens {
public List<List<String>> solveNQueens(int n) {
List<List<String>> ans = new ArrayList<List<String>>();
traceback(n,0,new LinkedList<Integer>(),ans);
return ans;
}
private void traceback(int n,int row,List<Integer> track,List<List<String>> board){
if(row == n){
List<String> oneAns = new ArrayList<String>();
for (int i = 0; i < n; i++) {
StringBuilder builder = new StringBuilder();
for (int j = 0; j < n; j++) {
// 获取每一行皇后所在的列
if(j == track.get(i)){
builder.append('Q');
}else{
builder.append('.');
}
}
String boardRow = new String(builder);
oneAns.add(boardRow);
}
board.add(oneAns);
}
for (int j = 0; j < n; j++) {
if(isVaild(row,j,track)){
track.add(j);
traceback(n,row+1,track,board);
track.remove(track.indexOf(j));
}else {
continue;
}
}
}
boolean isVaild(int row,int col,List<Integer> track){
// 判断同列有没有皇后
for (int i = 0; i < row; i++) {
if (track.get(i) == col) {
return false;
}
}
// 判断对角线是否有皇后
for (int i = 0; i < row; i++) {
if ((track.get(i) - col) == (row - i) || (track.get(i) - col) == (row - i) * -1) {
return false;
}
}
return true;
}
}