八皇后问题
问题描述
时间退回到1848年,国际西洋棋棋手马克斯·贝瑟尔提出了这样的一个问题:
在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
后面陆续有不同的学者提出自己的见解。大数学家高斯认为一共有76种摆法,1854年在柏林的象棋杂志上不同的作者发表了共计40种不同的见解,后来还有人利用图论的方法得出共有92种摆法.。
在数据结构与算法中,八皇后问题是一个经典使用回溯思想解决问题的案例。
回溯思路
我们发现在一个8*8的棋盘上,要放置8个皇后那么肯定是每一行都只会放置一个皇后,所以我们再放置皇后的过程中,我们每次都从这一行的第一个位置放置 ,查看是否与之前的发生冲突,冲突就这一行的下一个,如果这一行的都无法放置,那么回溯至上一个皇后的位置,重新在一行放置下一个位置试试,,,放置这个皇后成功后我们重新放置下一个皇后。。。
代码实现
通过上面的回溯分析,我们发现除了一个放置皇后位置的方法还需要一个检查该皇后位置时候冲突的方法 ,便于我们每次递归到下一个位置的时候检查是否冲突。
/** @Author: 云萧YYY @DateTime: 2021/08/14 @Description: 回溯法解决八皇后问题 */
public class Queen {
private int max = 8;
/** array 是这样子的一个数组 {0,4,7,5,2,6,1,3} ,此时这个数组的i表示棋盘的行 */
private int[] array = new int[max];
public static void main(String[] args) {
//
Queen queen = new Queen();
// 从第一个皇后开始寻找
queen.check(0);
}
/**
* *
*
* @param n 1.第几个皇后表示,当前的皇后是第几个
*/
public void check(int n) {
// 如果是第九个皇后那么我就结束
if (n == 8) {
printArray();
return;
}
/** 循环棋盘的每一行 */
for (int i = 0; i < 8; i++) {
/** 将第n个皇后的位置记录下来,放入数组,看看是否发生冲突 */
array[n] = i;
/** 如果这一个皇后放着不会产生冲突 ,那么看下一皇后 */
if (judge(n)) {
check(n + 1);
}
}
}
/**
* * 判断这个皇后是否可以放在这个棋盘上的这个位置
*
* @param n 第几个皇后
* @return
*/
public boolean judge(int n) {
// 由于每个皇后在棋盘上的每一行只能放一个,
for (int i = 0; i < n; i++) {
// 判断 是否在同一列上 和对角线上
if (array[i] == array[n] || Math.abs(i - n) == Math.abs(array[i] - array[n])) {
return false;
}
}
return true;
}
public void printArray() {
for (int i = 0; i < array.length; i++) {
//
System.out.print(array[i] + ",");
}
System.out.println();
}
}
代码说明
- 我们定义了一个数组,来存放八个皇后的位置,使用array[i],表示当前在第几列上,i表示第几行。
- 由于我们使用一维数组来表示这个棋盘的位置,以及皇后的位置信息,那么我们在判断位置是否冲突的方法也变得简单和不同。
- 我们需要遍历放置这个皇后之前位置的数组,如果又相同的那么说 在同一列上, Math.abs(i - n) == Math.abs(array[i] - array[n] 这个如果学过数学的都知道 对角线位置 x的变化量 与y的变化量时一致 所以 在这里借鉴了数学上的正比例函数斜率为1 ,即表示 行数的变化量 等于列数的变化量时 说明在同一斜线上。