递归--八皇后问题(回溯算法)

递归–八皇后问题(回溯算法)

问题:

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

思路分析
  1. 第一个皇后放在第一行的第一列
  2. 第二个皇后从第二行第一列开始放,然后判断可不可以,可以,就放第三个皇后,也从第三行第一列开始放;不可以在换下一个位置,在判定
  3. 放第三个皇后,还是从第三行的每一列开始放,找到正确位置,开始放第四个。。。。一直放到第八个皇后,然后开始回溯,直到找到所有的解
  4. 然后第一个皇后再换位置,放到第一行的第二列,再开始上述方法进行回溯,直到放到最后一列。
  5. 说明:理论上使用二维数组模拟棋盘,但实际上可以通过算法用一个一维数组来解决问题。arr[8] = {1,2,3,4,5,6,7,0}每个数组元素的值代表放第几列,数组元素的顺序对应着放在第几行,如:arr【0】 = 1,表示第一行第二列。

代码实现

package queen;

public class TwoDimension {
    //定义一个max表示共有多少个皇后
    static int max = 8;
    static int[] array = new int[max];
    public static void main(String[] args) {
        setQueen(0);


    }
    //根据思路分析建造棋盘--- 放皇后----判定是否冲突---放皇后
    //编写方法来放置皇后
    private static void setQueen(int hang){
        //总共是要放八个皇后,八个循环
        if (hang == 8){
            print();
            System.out.println("到达终点");
            //如果是第九个可以放才推出递归,那么要是第八个可以,但是第九个不可以,那不就完蛋了吗
            //所以,到了第九个就可以了
            return;
            //似乎要是全的话,不仅仅是第七行,最后一行,应该是最后一行的后面一行
        }
        for(int i = 0;i < 8;i ++){
            System.out.println("开始递归" + i + "lie" + hang + "hang");
            array[hang] = i;
            //最外面放置行,这样放置有问题,放第一个,第一个固定,再放第二个,在固定
            //他是在上一个固定的基础上再放下一个,我这样就是依次遍历
            if(isOk(hang)){
                setQueen(hang + 1);
            }
            //当前的行列要是不成立就再换一个,下一列
            //其实我居然看了一下我才会做啊

        }
    }
    //判断哪个是否冲突的方法
    private static boolean isOk(int m){
        //m表示当前传入的是第几行,n表示为第几列
        //遍历判断是否相等吗?
        boolean isFlag = true;
        for (int i = 0;i < m;i ++){
            //i表示为第几行地皇后
            if(array[i] == array[m]){
                System.out.println("是否同列");
                //第一行所在皇后的对应的列是不合法的
                isFlag =  false;
            }else if(i == m){
                //第i行是不合法的
                isFlag =  false;
                //关于斜边对应不合法,可以用数学地关系来推导
                //如果行数与列数的差相等,那就是相等
                //不用像原来一样推导
            }else if(Math.abs(i- m)==Math.abs(array[m]- array[i])){
                System.out.println("是否同斜线");
                //斜边对应的都是不合法的
                //只能写对应斜边地第一格
                isFlag =  false;
            }else{
                isFlag =  true;
            }
        }
        return isFlag;
    }
    //传入一个参数比较好,并表示为行数,对应的数组坐标值即为对应的列数,传入两个太混乱
    //传入一个,就不用判断是否在同一行,因为你每一次都是再上上一行放的,本来就不在同一行
    //算法背后是数学在作用,不能死算,要学会找对应的规律

    //方法门将皇后的位置打印出来
    private static void print(){
        for(int i = 0;i < array.length;i ++){
            System.out.print(array[i]  + "  ");
        }
        System.out.println();
    }
}

运行结果:
运行结果
分析:正常来说,进入了同列判定的语句,说明该位置与之前的棋子同列,故而不能放在那里,换下一列。但是还进行判定,说明,一直走到底。问题在于,只要有一个不符合条件的那就跳出循环。
修改:在对应的判断同列,同斜线语句中加上跳出语句。

二次运行:
结果:
运行结果
分析:成功了,当不正确的情况会自动回溯的,直到检索到所有的结果

再次修改:增加可能结果的总数

正确结果
最终代码:

package queen;

public class TwoDimension {
    //定义一个max表示共有多少个皇后
    static int max = 8;
    static int[] array = new int[max];
    static int count = 0;
    public static void main(String[] args) {
        setQueen(0);
        System.out.println("总数为" + count);


    }
    //根据思路分析建造棋盘--- 放皇后----判定是否冲突---放皇后
    //编写方法来放置皇后
    private static void setQueen(int hang){
        //总共是要放八个皇后,八个循环
        if (hang == 8){
            count ++;
            print();
            //System.out.println("到达终点");
            //如果是第九个可以放才推出递归,那么要是第八个可以,但是第九个不可以,那不就完蛋了吗
            //所以,到了第九个就可以了
            return;
            //似乎要是全的话,不仅仅是第七行,最后一行,应该是最后一行的后面一行
        }
        for(int i = 0;i < 8;i ++){
            //System.out.println("开始递归" + i + "lie" + hang + "hang");
            array[hang] = i;
            //最外面放置行,这样放置有问题,放第一个,第一个固定,再放第二个,在固定
            //他是在上一个固定的基础上再放下一个,我这样就是依次遍历
            if(isOk(hang)){
                setQueen(hang + 1);
            }
            //当前的行列要是不成立就再换一个,下一列
            //其实我居然看了一下我才会做啊

        }
    }
    //判断哪个是否冲突的方法
    private static boolean isOk(int m){
        //m表示当前传入的是第几行,n表示为第几列
        //遍历判断是否相等吗?
        boolean isFlag = true;
        for (int i = 0;i < m;i ++){
            //i表示为第几行地皇后
            if(array[i] == array[m]){
                //System.out.println("是否同列");
                //第一行所在皇后的对应的列是不合法的
                isFlag =  false;
                break;
            }else if(i == m){
                //第i行是不合法的
                isFlag =  false;
                break;
                //关于斜边对应不合法,可以用数学地关系来推导
                //如果行数与列数的差相等,那就是相等
                //不用像原来一样推导
            }else if(Math.abs(i- m)==Math.abs(array[m]- array[i])){
               // System.out.println("是否同斜线");
                //斜边对应的都是不合法的
                //只能写对应斜边地第一格
                isFlag =  false;
                break;
            }else{
                isFlag =  true;
            }
        }
        return isFlag;
    }
    //传入一个参数比较好,并表示为行数,对应的数组坐标值即为对应的列数,传入两个太混乱
    //传入一个,就不用判断是否在同一行,因为你每一次都是再上上一行放的,本来就不在同一行
    //算法背后是数学在作用,不能死算,要学会找对应的规律

    //方法门将皇后的位置打印出来
    private static void print(){
        for(int i = 0;i < array.length;i ++){
            System.out.print(array[i]  + "  ");
        }
        System.out.println();
    }
}

总结分析:

  1. 根据思路分析开始写方法,放皇后,和判断皇后是否合理两个方法
  2. 在判定合理的过程总,一旦遇到非法的状况,就跳出,输入下一列,而不是从从头遍历到尾
  3. 在判定是否为同一斜线的过程中,学会发现一般性的简单的常见的逻辑,而不是把所有情况都列出来
  4. 在书写放皇后的方法中,先列一列一般不带递归的语句,发现共同逻辑,再根据你所写的语句提炼出递归
  5. 关于递归,必须具备三要素
    • 递归终止条件:在这里是八个棋子放满,容易忽略的是第八个棋子也要正确,故而把终止条件放在第九个棋子
    • 递归终止时的处理方法:如果放成功下一步干什么,没放成功下一步有干什么
    • 提取重复逻辑:在所列的那么多的一般语句中发现不停的出现两个语句,for和if,
  6. 确实用一维数组更加方便一点,一维数组有两个量分别是数组下标和当前下标的值,刚好可以表示当前的皇后的位置。而且在循环中还把列给单提出来,确实更加方便

错误补充:
忘记给行赋值对应的列时,就会什么都不出!
错误代码:
错误代码
运行结果:
运行结果

发布了19 篇原创文章 · 获赞 3 · 访问量 613

猜你喜欢

转载自blog.csdn.net/Blackoutdragon/article/details/103895086