前言
迷宫问题虽然也用到了递归,但它比较简单,我感觉八皇后问题才真正的体现了递归的深度难度,也体现了回溯的重要性,本文将着手揭开八皇后问题中递归的真面目
一、问题描述
在一个大小为8*8的国际象棋棋盘中,摆放着八个皇后,要求把八个皇后摆放到棋盘中的不同位置,使得皇后之间不能互相攻击,如果有两个皇后摆放在同一行或同一列或同一斜线,则表示这两个皇后可以互相攻击,请问该棋盘总共有多少种摆法。
二、思路分析
该问题可能会有很多的解法,我以我们人最容易想到的一种方式描述一遍,如果一开始就想计算机该如何用递归实现它就很难。首先我们可以把第一个皇后放到第一行的任意一列,因为第一个皇后不可能和还未放置的皇后发生冲突,然后我们就放置第二个皇后,这时我们放的时候就要小心了,因为放的第二个皇后可能会跟第一个放置好的皇后发生冲突,如果冲突了我们就要放到其它的位置。这个判断是否发生冲突的操作计算机跟我们一样,都是一样的判断,而计算机的最重要任务是判断所有的放置可能,把未冲突的放置筛选出来,如何用计算机实现这步操作就考验我们对递归回溯的理解运用程度了。
三、过程图解
递归回溯过程比较复杂,图中并没有展示所有细节,还需要加上自己的理解才能充分领会整个过程,希望能对不懂的朋友有所帮助。
四、测试源码
package com.yc.recursion;
/**
* map表示一个解,即8个皇后分别放在第几行第几列
* 例如map[0][1]表示第一个皇后放在第一行的第二列
* max 表示棋盘有几行几列
* count 表示总共有多少种解法
* judgeCount 表示判断一个皇后放到一个地方是否会发生冲突判断了多少次
* @author lsj
*
*/
public class Queen8 {
int max = 8;
static int count = 0;
static int judgeCount = 0;
int [] map = new int[max];
public static void main(String[] args) {
Queen8 q8 = new Queen8();
//下标从0开始,从第一行开始摆放皇后,然后判断摆放后是否会发生冲突,最终得出解
q8.check(0);
System.out.printf("共有%d种解法",count);
System.out.println();
System.out.printf("共判断了%d次",judgeCount);
}
/**
* 递归判断所有行,判断到第9行时得出一个解,然后回溯把皇后放到不同的列继续查找其它的解
* @param n 表示第几行
*/
public void check(int n){
//当8行都已经摆放好了皇后,判断到第九行时表明已经得出一个解,回溯把皇后摆到不同的列继续寻找其它的解
if(n==max){
//打印一个解,8个皇后分别摆放到几行几列为一个解
print();
return;
}
for(int i=0;i<max;i++){
//第n个皇后放到第i列
map[n] = i;
//判断皇后放到此处后是否会与已经摆放好的皇后发生冲突
if(judge(n)){
//如果没有发生冲突,就继续在下一行放置一个皇后
check(n+1);
}
}
}
/**
* 判断皇后摆放后是否会发生冲突
* @param n 第几个皇后
* @return
*/
private boolean judge(int n){
//判断次数+1
judgeCount++;
//把当前摆放的皇后跟前面摆放好了的每一个皇后进行比较,看是发生冲突
for(int i=0;i<n;i++){
//判断当前皇后与已经放好的第i个皇后是否在同一行,同一列,同一斜线
//如果是就会发生冲突,不是就表明可以把当前皇后放到当前列,继续判断下一个皇后的摆放位置
//行间距与列间距相等就表明在同一斜线
if(map[i]==map[n]||Math.abs(i-n)==Math.abs(map[i]-map[n])){
return false;
}
}
return true;
}
/**
* 打印一个解
*/
private void print(){
//打印一个解就+1
count++;
//把8个皇后分别摆放的位置打印出来
for(int i=0;i<max;i++){
System.out.print(map[i]+" ");
}
System.out.println();
}
}
总结
这个问题有点难度,自己搞懂挺不容易,如果有不懂的朋友也可以像我一样把问题写出来,这样也许更有收获。