前几天在学习数据结构与算法的课程中,B站视频老师也讲到了N皇后问题,不过没有深入讲,说是后面会有一节课会专门讲N皇后问题,今天做这个算法就遇到了这个问题,那就先解决一下吧,这道题我在B站上找了几个人讲的解决方法,都不太一样。
回溯算法是搜索算法中一种控制策略,基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直到得到解或者是证明无解。
问题描述
这是来源于国际象棋的一个问题,n皇后问题要求在一个n*n的棋盘上放置n个皇后,使得它们彼此不受攻击。按照国际象棋的规则,一个皇后可以攻击与之处在同一行或者同一列或者同一斜线上的其他任何棋子。
问题分析
我用最简单的4*4格棋盘4皇后为例进行讲解,求解过程总从空棋盘开始,设从第一行开始,第一个皇后默认位置在第一列,以后改变时,顺序选择第2列,第3列,第4列。当下一个皇后找不到合适的列位置时,就要回溯,去改变前一个皇后的位置。
代码分析
回溯法解决N皇后问题
使用一个一维数组表示皇后的位置
其中数组的下标表示皇后的所在的行
数组元素的值表示皇后所在的列
这样设计的棋盘,所有皇后必定不在同一行
假设前n-1行的皇后已经按照规则排列好
那么可以使用回溯法逐个试出第n行皇后的合法位置
所有皇后的初始位置都是第0列
那么逐个尝试就是从0到N-1
如果达到N,仍未找到合法位置
那么就置当前行的皇后的位置为初始位置0
然后回退一行,且该行的皇后的位置加1,继续尝试
如果目前处于第0行,还要再回退,说明此问题已再无解
如果当前行的皇后的位置还是在0到N-1的合法范围内
那么首先要判断该行的皇后是否与前几行的皇后互相冲突
如果冲突,该行的皇后的位置加1,继续尝试
如果不冲突,判断下一行的皇后
如果已经是最后一行,说明已经找到一个解,输出这个解
然后最后一行的皇后的位置加1,继续尝试下一个解
class Solution {
//记录某一列是否有皇后摆放
private boolean col[];
//记录某条正对角线(左上右下)是否已有皇后摆放(某条对角线对应的摆放位置为x-y+n-1)
private boolean dia1[];
//记录某条斜对角线(左下右上)是否已经有皇后摆放了(某条对角线的对应的摆放位置为x+y)
private boolean dia2[];
public int totalNQueens(int n) {
col = new boolean[n];
dia1 = new boolean[2*n-1];
dia2 = new boolean[2*n-1];
return putQueen(n,0);
}
//递归回溯方式摆放皇后
//n 待摆放的皇后个数
//index 已摆放的皇后个数
private int putQueen(int n,int index){
int res=0;
if(index==n){
return 1;
}
//表示在index行的第i列尝试摆放皇后
for(int i=0;i<n;i++){
if (!col[i] && !dia1[i - index + n - 1] && !dia2[i + index]){
//递归
col[i]=true;
dia1[i-index+n-1]=true;
dia2[i+index]=true;
res+=putQueen(n,index+1);
//回溯
col[i]=false;
dia1[i-index+n-1]=false;
dia2[i+index]=false;
}
}
return res;
}
public static void main(String[] args){
int n = new Solution().totalNQueens(8);
System.out.println(n);
}
}