<算法>递归——八皇后问题(java实现)

问题提出

在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不能处于同一行、同一列或者是同一斜线,问有多少种摆法

思路分析

  1. 第一个皇后先放在第一行第一列

  2. 第二个皇后放在第二行第一列,然后判断是否符合规则,若不符合,则放在第二行第二列、第三列…直到放置的位置合理

  3. 第三个皇后放在第三行第一列,然后判断是否符合规则,若不符合,则放在第三行第二列、第三列…直到放置的位置合理,剩下的皇后也是如此进行,直至8个皇后都能放在互相不冲突的位置,那么就得到了一个正确解

  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,将第一个皇后放在第一列的所有正确解都得到

  5. 然后继续将第一个皇后放在第一行第二列,循环执行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();
    }
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值