八皇后问题介绍
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋手马克思.贝瑟尔与1848年提出: 在8x8格的国际象棋上摆放八个皇后,使其不能相互攻击,即: 任意两个皇后都不能处于同一行,同一列或同一斜线上,问有多少种摆法
思路分析
- 第一个皇后先放第一行第一列
- 第二个皇后放在第二行第一列,然后判断是否ok,如果不ok,继续放在第二列,第三列,依次把所有列都放完,找到一个合适
- 继续第三个皇后,还是第一列,第二列…直到八个皇后也能放在一个不冲突的位置,算是找到一个解
- 当得到一个正确解时,在栈回退到上一个栈是,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到
- 然后回头继续第一个皇后放第二列,后面继续循环执行1,2,3,4的步骤
- 说明: 理论上讲应该创建一个二维数组来表示,但是一个一维数组也可以
代码
package recursion;
public class Queen8 {
// 定义一个max表示共有多少个皇后
int max = 8;
// 定义一个数组 array, 保存皇后放置位置的结果
static int count = 0;
static int judgeCount = 0;
int[] array = new int[max];
public static void main(String[] args) {
// 测试一把, 8皇后是否正确
Queen8 queen8 = new Queen8();
queen8.check(0);
System.out.printf("一共有%d解法",count);
System.out.printf("一共判断冲突的次数%d",judgeCount);
}
// 编写一个方法,放置第n个皇后
// 特别注意 check 每一层是每一次递归时进入到check方法中都有一套for循环,因此会有回溯
private void check(int n) {
if (n == max) { // n == 8 在放第九个皇后, 其实8个皇后就已经放好了
print();
return;
}
// 依次放入皇后,并判断是否冲突
for (int i = 0; i < max; i++) {
// 先把当前皇后 n 放到该行的第1列
array[n] = i;
// 判断当放置第n个皇后到i列时,是否冲突
if (judge(n)) { // 不冲突
// 接着放 n + 1个皇后,就开始递归
check(n+1); // 有 8个皇后同一层中就会有8个for循环
}
// 如果冲突,就继续执行array[n] = i; 就是将第n个皇后放置在本行的后一个位置
}
}
// 查看当我们放置第n个皇后时,就去检测该皇后是否和前面已经摆放的皇后是否冲突
/**
*
* @param n 表示第n个皇后
* @return
*/
private boolean judge(int n) {
judgeCount++;
for (int i = 0; i < n; i++) {
// array[i] == array[n] 表示判断第n个皇后是否在同一列
// Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判断第n个皇后是否和第i个皇后是否在同一条斜线
// 判断同一行,没有必要, n每次都在递增
if (array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n]-array[i])){
return false;
}
}
return true;
}
// 写一个方法,可以将皇后摆放的位置
private void print(){
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}
图文解析
第一个皇后先放第一行第一列,(我们以第一个答案为例子)
第二个皇后放在第二行第一列,不行就往下一列
这里对应的是
if (array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n]-array[i])){
return false;
}
array[i] == array[n] 代表前面的元素跟当前元素同一列
Math.abs(n-i) == Math.abs(array[n]-array[i]) 代表有元素在对角线上重合(不信的话随便代几个数进去试试)
最后得到下面的结果通过,继续往下走
重复上面的步骤,得到下列的步骤(请看思路并自行理解)
来到这里之后,我们会发现到第六行无论怎么样都无法找到一个跟下面五个皇后不同行不同列不同斜线的皇后。于是我们要回到第五行,让第五行后移
得到下图,此时第七行也找不到满足条件的皇后,然而第六行的皇后只能放在这个点,第五行的皇后已经遍历完毕,所以我们要回到第四行,让第四行的皇后往后移
然后继续往上布置皇后
来到这里又嗝屁后,我们依旧要从第七行,第六行,第五行依次遍历返回(回溯),直到回到第二行第五列的时候找到答案
存入数组后,我们又得从第八行往后移一位继续找答案,直到第一行的皇后遍历完才结束。
所以
for (int i = 0; i < max; i++) {
// 先把当前皇后 n 放到该行的第1列
array[n] = i;
// 判断当放置第n个皇后到i列时,是否冲突
if (judge(n)) { // 不冲突
// 接着放 n + 1个皇后,就开始递归
check(n+1); // 有 8个皇后同一层中就会有8个for循环
}
// 如果冲突,就继续执行array[n] = i; 就是将第n个皇后放置在本行的后一个位置
}
我们看for循环就可以认为是当前行的皇后在后移,if语句判断该点是否可以放置皇后,如果可以就到下一行布置皇后,如果不行就继续后移,如果下一行的皇后布置成功,那么下下一行的皇后就会继续布置,直到当前行的for循环结束
当我们n来到max的时候,也结束
if (n == max) { // n == 8 在放第九个皇后, 其实8个皇后就已经放好了
print();
return;
}
大功告成!!
最后结果为92种