问题提出
在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不能处于同一行、同一列或者是同一斜线,问有多少种摆法
思路分析
-
第一个皇后先放在第一行第一列
-
第二个皇后放在第二行第一列,然后判断是否符合规则,若不符合,则放在第二行第二列、第三列…直到放置的位置合理
-
第三个皇后放在第三行第一列,然后判断是否符合规则,若不符合,则放在第三行第二列、第三列…直到放置的位置合理,剩下的皇后也是如此进行,直至8个皇后都能放在互相不冲突的位置,那么就得到了一个正确解
-
当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,将第一个皇后放在第一列的所有正确解都得到
-
然后继续将第一个皇后放在第一行第二列,循环执行1~4步
可以使用一个二维数组,一维下标表示行,二维下标表示列。但是由于可以将一维下标i表示行,而将下标对应的值表示皇后所放在的列,因此可以使用一维数组即可。
例如:arr[8]={0,4,7,5,2,6,1,3}
就是一种正确放置解法
这个数组表示的意思是:
- 第一个皇后放在第一行第一列(0,0)
- 第二个皇后放在第二行第五列(1,4)
- 第三个皇后放在第三行第八列(2,7)
- 第四个皇后放在第四行第六列(3,5)
- 第五个皇后放在第五行第三列(4,2)
- 第六个皇后放在第六行第七列(5,6)
- 第七个皇后放在第七行第二列(6,1)
- 第八个皇后放在第八行第四列(7,3)
解决流程
八皇后问题即合理放置皇后在格子中,然后递归得到所有放置的解法
1、编写judge函数,检查放置位置是否合理
①当放置一个皇后时,就去检测当前将要放置的位置是否与前面放置的皇后位置有冲突
②发生冲突的放置有3种类型:
- 任意两个皇后在同一行
- 任意两个皇后在同一列
- 任意两个皇后在同一斜线
但是由于我们使用的是一维数组,并且放置皇后时我们是依照每行放置一个皇后,即一维数组的下标就表示行,因此不会出现任意两个皇后在同一行的情况,因此只需要判断后两种情况即可
代码如下:
//当放置一个皇后时,就去检测当前将要放置的位置是否与前面放置的皇后位置有冲突
//n表示的是第几个皇后,0~7
public static boolean judge(int n)
{
for(int i=0;i<n;i++)
{
//1、arr[n]==arr[i],检测皇后是否在同一列
//2、Math.abs(n-i)==Math.abs(arr[n]-arr[i]),检测是否在同一斜线
if(arr[n]==arr[i] || (Math.abs(n-i)==Math.abs(arr[n]-arr[i])))
{
return false;
}
}
return true;
}
2、编写check函数,放置第n个皇后
①由于是8皇后,因此当合理放置皇后至第8个时,就得到了一种放置的解法,就输出得到的结果
②每次放置皇后是从第1列开始,然后判断放置位置是否合理
代码如下:
//放置第n个皇后
public static void check(int n)
{
//如果n=8,则说明八个皇后都已经放置在正确的位置,已得到一种解法,则输出
if(n==8)
{
printResult();
}
//依次放入皇后,并判断是否冲突
else
{
for(int i=0;i<8;i++)
{
//由于每次放置皇后都是从第1列开始,因此先把当前皇后n放在第1列
arr[n]=i; //i从0开始,因此这就能表示先是把皇后n放在第1列
//判断放置位置是否合理
//如果放置位置合理,则放置下一个皇后
if(judge(n))
{
check(n+1);
}
//如果放置不合理,则会向右移动,即跳转继续执行for循环
}
}
}
3、编写输出皇后放置的结果的函数
其中count是static全局变量,用于统计解法的数量,每打印一次结果说明得到一个放置八皇后的解法
//打印结果
public static void printResult()
{
count++;
for(int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
}
4、代码整体结构
5、运行结果
…
可以看出是先得到第一个皇后放在第一列的所有解法,再得到第一个皇后放在第二列的所有解法,…,再得到第一个皇后放在第八列的所有解法
6、源代码
package recurDemo;
import java.util.*;
public class Queen8 {
static int arr[]=new int[8];
static int count=0; //统计结果个数
public static void main(String[] args) {
//利用一维数组来表示皇后的放置位置
check(0);
System.out.println("一共有"+count+"次放置方法");
}
//放置第n个皇后
public static void check(int n)
{
//如果n=8,则说明八个皇后都已经放置在正确的位置,已得到一种解法,则输出
if(n==8)
{
printResult();
}
//依次放入皇后,并判断是否冲突
else
{
for(int i=0;i<8;i++)
{
//由于每次放置皇后都是从第1列开始,因此先把当前皇后n放在第1列
arr[n]=i; //i从0开始,因此这就能表示先是把皇后n放在第1列
//判断放置位置是否合理
//如果放置位置合理,则放置下一个皇后
if(judge(n))
{
check(n+1);
}
//如果放置不合理,则会向右移动,即跳转继续执行for循环
}
}
}
//当放置一个皇后时,就去检测当前将要放置的位置是否与前面放置的皇后位置有冲突
//n表示的是第几个皇后,0~7
public static boolean judge(int n)
{
for(int i=0;i<n;i++)
{
//1、arr[n]==arr[i],检测皇后是否在同一列
//2、Math.abs(n-i)==Math.abs(arr[n]-arr[i]),检测是否在同一斜线
if(arr[n]==arr[i] || (Math.abs(n-i)==Math.abs(arr[n]-arr[i])))
{
return false;
}
}
return true;
}
//打印结果
public static void printResult()
{
count++;
for(int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
}
}