八皇后问题

八皇后问题:

  八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
这里写图片描述
  如上图所示,有一点需要注意,八个皇后不仅不在同一行和同一列,而且还不能在同一条对角线上(当然,如果某两个皇后在同一行或者同一列,那么它们就一定不在同一对角线了),这样一下子要问题的难度提升了。

  分析思路:摆放八个皇后,必然是每行每列只有一个皇后,那么我们就从第0行开始,一直摆放到第7行即可,每摆放一个皇后就检查是否符合问题要求。这个过程和递归的思路一致。
   在摆放第0行的时候,一共有8列,所以就有8种可能摆放,用一个for循环来遍历它。现在完成了第0行的摆放,然后进入下一行,这里要注意一点,因为题目要求,皇后不能在同一条对角线上,所以第1行的摆放方法并不是简单的7种,那是几种呢?这和第0行皇后的摆放位置有关系。为了递归程序更简洁,我们这里依然假设第1列有8中摆放,然后再对这每一种摆放进行检查,对不满足要求的摆放方式,终止这一路递归即可。顺着这个思路,依次递归下去,直到最后一行就可以了。
代码:

/*
 * N皇后问题
 * leetcode链接:https://leetcode.com/problems/n-queens/description/
 * 暴力破解,自顶向下,循环递归,试图优化
 */
package leetcode;

import java.util.ArrayList;

public class N_Queens_51 {
    public static void main(String[] args) {
        int N = 8;
        ArrayList<String[]> res = solveNQueens(N);
        //遍历ArrayList
        int num = 0;  //可能的总的摆放数目
        for(String[] temp : res) {
            num++;
            System.out.println("第" + num + "种摆放方法");
            for(int i = 0; i < temp.length; i++) {
                System.out.println(temp[i]);  
            }
        }  
    }
    private static ArrayList<String[]> solveNQueens(int N) {
        ArrayList<String[]> res = new ArrayList<String[]>();  //保存最终结果
        if(N == 0)
            return null;
        solveNQueens(N, 0, new int[N], res);
        return res;
    }
    private static void solveNQueens(int N, int row, 
                  int[] columnForRow, ArrayList<String[]> res) {
        if(row == N) {   //递归终止条件,对皇后的位置进行记录
            String[] item = new String[N];  
            for(int i = 0; i < N; i++) {  
                StringBuilder strRow = new StringBuilder();  
                for(int j = 0; j < N;j ++) {  
                    if(columnForRow[i] == j) strRow.append("& ");  
                    else                     strRow.append("* ");  
                }  
                item[i] = strRow.toString();  
            }  
            res.add(item);  
            return;
        }
        for(int i = 0; i < N; i++)  {  
            columnForRow[row] = i;  
            if(check(row,columnForRow)) {  
                solveNQueens(N, row+1, columnForRow, res);  
            }  
        } 
    }
    private static boolean check(int row, int[] columnForRow) { //判断列是否重复,或者在同一对角线上 
        for(int i=0;i<row;i++) {  
            if(columnForRow[row]==columnForRow[i] || Math.abs(columnForRow[row]-columnForRow[i])==row-i)  
                return false;  
        }  
        return true;  
    }  
}

  这是一个可以独立运行的Java类,主函数中定义了皇后的个数N,使用类型为String[]的链表ArrayList去存储最终的摆放结果,String[]是长度为8的字符串数组,每个元素存储的是类似这样的字符串:“ * * * & * * * * ”,&表示当前行皇后的摆放位置。for打印最终结果。
  solveNQueens()是两个重载函数,这样写是为了耦合更松,同时也方便递归函数的编写。solveNQueens(int N)接受参数,然后调用递归主体函数:solveNQueens(int N, int row, int[] columnForRow, ArrayList(这里泛型显示原因没写))
  递归主体中,开始的这个if函数是递归的结束条件,内部两个fou循环是为了把最终结果以字符串数组的形式存储到ArrayList中。

for(int i = 0; i < N; i++)  {  
            columnForRow[row] = i;  
            if(check(row,columnForRow)) {  
                solveNQueens(N, row+1, columnForRow, res);  
            }  
        } 

这是递归的最重要部分,针对每一行的8个列,循环8次,每一次拜访后检查(check)与之前几行的皇后位置是否矛盾,check函数:

private static boolean check(int row, int[] columnForRow) { //判断列是否重复,或者在同一对角线上 
        for(int i=0;i<row;i++) {  
            if(columnForRow[row]==columnForRow[i] || Math.abs(columnForRow[row]-columnForRow[i])==row-i)  
                return false;  
        }  
        return true;  
    }  

columnForRow[row]==columnForRow[i]判断列是否重复,Math.abs(columnForRow[row]-columnForRow[i])==row-i判断是否在同一个对角线上。

  观察代码,会发现这就是一个循环和递归的组合,在程序设计中,顺序结构、选择结构、循环结枸是最基本的,但是递归用的也很多。顺序结构与递归的组合容易理解,但是选择(if-else)、循环(for /while)与递归的组合比较难以理解,比如本例就是一个典型的循环和递归的组合:递归不是在一条线路上一步步走下去,而是多条递归顺序开展,这样做,递归的次数显然是指数增长的,有兴趣的可以给N取不同的值,观察程序执行情况。

此题解决思想核心:暴力破解,循环 * 递归,试图优化

程序执行结果:

第91种摆放方法
* * * * * * * & 
* * & * * * * * 
& * * * * * * * 
* * * * * & * * 
* & * * * * * * 
* * * * & * * * 
* * * * * * & * 
* * * & * * * * 
第92种摆放方法
* * * * * * * & 
* * * & * * * * 
& * * * * * * * 
* * & * * * * * 
* * * * * & * * 
* & * * * * * * 
* * * * * * & * 
* * * * & * * * 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值