面试题:八皇后问题(N皇后问题)「建议收藏」

面试题:八皇后问题(N皇后问题)

大家好,又见面了,我是全栈君。

前言

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?这道题目也可以稍微延伸一下,变为 N×N的棋盘上放置N个皇后,其他条件相同。
下面介绍一种比较简单易懂的实现方式。

项目下载地址

正文

算法

先说一下算法, 这里使用的是一个改良版的广度优先搜索算法。在N×N的棋盘上,我们先在第一行的第一个位置放置下皇后,接着我们就不去管第一行了,因为第一行已经不能放置皇后了。我们在第二行找到所有的可以放置皇后的位置。同理我们现在可以不用去管前两行了。我们对于第二行的每一个可以放置皇后的位置,都在第三行继续寻找可以放置皇后的位置,如此往复,直到我们遍历到最后一行。这个时候我们就得到了一部分解,这些解是对于第一个皇后放置在第一行第一列的位置而言。接下来对于第一行第二列、第三列…所有列都进行这个步骤,就得到了所有的解。

代码

为了更加直观,我们模拟出一个N×N的棋盘。我们把每次放置一个皇后之后的局面称为一个状态(State)。下面是State类的代码:

import java.util.ArrayList;
import java.util.List;

public class State { 
     
    private List<Point> pointList = new ArrayList<Point>();
    private int lineNum;

    public List<Point> getPointList() {
        return pointList;
    }

    public int getLineNum(){
        return lineNum;
    }

    public void setLineNum(int lineNum){
        this.lineNum = lineNum;
    }

}

每个state对象有两个属性,pointList存放的是当前的state下已经放置的皇后坐标,lineNum是当前state所遍历到的行数。其中用到的Point类代码如下:

public class Point{ 
     
    private int X;
    private int Y;

    public Point(int x, int y){
        this.X = x;
        this.Y = y;
    }

    public int getX(){
        return this.X;
    }

    public int getY(){
        return this.Y;
    }

    public void setX(int x){
        this.X = x;
    }

    public void setY(int y){
        this.Y = y;
    }

}

每个Point对象有一个X坐标和一个Y坐标。
下面是主程序:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class EightQueen { 
     
    //起始状态列表
    public static List<State> startStates = new ArrayList<State>();

    //棋盘的行列数和要放置的皇后数量
    public static final int lineNum = 4;

    //一个N×N的棋盘
    public static Point[][] allPoints = new Point[lineNum][lineNum];

    //解法数量
    public static int count = 0;

    public static void main(String[] args) {

        //初始化棋盘
        for(int i=0; i<lineNum; i++){
            for(int j=0; j<lineNum; j++){
                allPoints[i][j] = new Point(i, j);
            }
        }

        //初始化起始状态列表。每个State的PointList分别存放了第一行的8个坐标,并且设置第一行为遍历初始行
        for(int i=0; i<lineNum; i++){
            State state = new State();
            state.getPointList().add(new Point(0, i));
            state.setLineNum(0);
            startStates.add(state);
        }

        //对于初始化state列表中的每个state,进行遍历操作。
        for(State state : startStates){
            calculate(state);
        }
        System.out.println("总数为:" + count); 
    }

    public static void calculate(State state)
    {
        Stack<State> stack = new Stack<State>();
        stack.push(state);
        while(!stack.isEmpty()){
            //从stack里取出一个状态
            State state2 = stack.pop();
            //如果已经遍历到最后一行,输出这个解
            if(state2.getLineNum() == lineNum - 1){
                for(Point goalpoint : state2.getPointList()){
                    for(int i=0; i<lineNum; i++){
                        if(i!=goalpoint.getY())
                            System.out.print("_ ");
                        else
                            System.out.print("Q ");
                    }
                    System.out.println(); 
                }
                System.out.println();
                count++;
                continue;
            }

            //否则寻找下一行可以放置皇后的位置
            int currentLineNum = state2.getLineNum() + 1;
            for(Point point : allPoints[currentLineNum]){
                //如果该点可以放置皇后
                if(isSatisfied(point, state2.getPointList()))
                {
                    //创建一个state对象
                    State newState = new State();
                    //把这个新的state的pointList设置为前一个点的pointList里的所有点加上当前的点的坐标
                    for(Point point2 : state2.getPointList()){
                        newState.getPointList().add(new Point(point2.getX(), point2.getY()));
                    }
                    newState.getPointList().add(point);
                    //设置新的state的行数为下一行
                    newState.setLineNum(currentLineNum);
                    //入栈
                    stack.push(newState);
                }
            }
        }
    }

    //判断一个点是否可以放置皇后
    public static boolean isSatisfied(Point point, List<Point> list){
        for(Point point2 : list){
            //两个皇后不能再同一条横线、直线、斜线上。由于我们直接遍历的是下一行的点,所以肯定不会出现X坐标相同的情况
            if(point2.getY() == point.getY() 
                    || Math.abs(point2.getX() - point.getX()) == Math.abs(point2.getY() - point.getY()))
                return false;
        }
        return true;
    }

}

程序的输出为

_ Q _ _ 
_ _ _ Q 
Q _ _ _ 
_ _ Q _ 

_ _ Q _ 
Q _ _ _ 
_ _ _ Q 
_ Q _ _ 

总数为:2

如果我们更改lineNum为6,输出为

_ Q _ _ _ _ 
_ _ _ Q _ _ 
_ _ _ _ _ Q 
Q _ _ _ _ _ 
_ _ Q _ _ _ 
_ _ _ _ Q _ 

_ _ Q _ _ _ 
_ _ _ _ _ Q 
_ Q _ _ _ _ 
_ _ _ _ Q _ 
Q _ _ _ _ _ 
_ _ _ Q _ _ 

_ _ _ Q _ _ 
Q _ _ _ _ _ 
_ _ _ _ Q _ 
_ Q _ _ _ _ 
_ _ _ _ _ Q 
_ _ Q _ _ _ 

_ _ _ _ Q _ 
_ _ Q _ _ _ 
Q _ _ _ _ _ 
_ _ _ _ _ Q 
_ _ _ Q _ _ 
_ Q _ _ _ _ 

总数为:4

由于lineNum = 8的时候输出太长,这里不做展示。结果的数量为92种。
这里附上不同lineNum对应的解法数量:

lineNum     solution(lineNum)
1           1  
2           0  
3           0  
4           2  
5           10  
6           4  
7           40  
8           92  
9           352  
10          724  
11          2680  
12          14200  
13          73712  
14          365596  
15          2279184  
16          14772512  
17          95815104  
18          666090624  
19          4968057848 
20          39029188884  
21          314666222712  
22          2691008701644  
23          24233937684440  
24          227514171973736  
25          2207893435808352  

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108336.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 怎么通过计算机名查别人ip_计算机怎么看ip地址

    怎么通过计算机名查别人ip_计算机怎么看ip地址经过几个月实践,更新如下:首先我的目标是:1:不给客户端装任何软件,即用户信息肯定是全自动生成。2:由我们IT去查询IP,计算机名,用户名任意一项能查出其他两项信息。3:最好是不花钱,SCCM虽然可以查,但毕竟是商业的,能省下钱当然最好啦。以下是四种方法,各有各的好,难度也越来越大和复杂,在这里说下我实践下来的做法。初级篇:最简单的方法,本帖里很多朋友也说了,组策略批处理给客户端运行,生成IP,计…

    2022年10月10日
    0
  • 嵌入式学习网站

    嵌入式学习网站创客智造:https://www.ncnynl.com/中国电子网:http://www.21ic.com/中国单片机公共实验室:http://www.bol-system.com/驱动开发网:http://www.driverdevelop.com/first.phpQT中文论坛:http://www.qtcn.org/bbs/i.php水木清华研究中心:http://w…

    2022年5月9日
    53
  • apap图像全景拼接

    apap图像全景拼接文章目录基本原理图像配准关于最小割关于最大流apap的实现流程代码实现实验场景场景一场景二遇到的问题基本原理图像配准图像配准(apap)是将两张场景相关的图像进行映射,寻找其中的关系,多用在医学图像配准、图像拼接、不同摄像机的几何标定等方面,其研究也较为成熟。OpenCv中的stitching类就是使用了2007年的一篇论文(Automaticpanoramicimagestitchi…

    2022年9月22日
    5
  • IM在线客服系统_开源在线客服系统附源码

    IM在线客服系统_开源在线客服系统附源码  在线客服系统比以往任何时候都更受欢迎,随着即时通讯巨头WhatsApp、FacebookMessenger和微信的崛起,即时通讯平台也正在接管商业通信。精简和用户友好的消息传递解决方案已经取代了电话、传真和电子邮件,它们使团队能够更成功地沟通和协作,即使是在混合的工作环境中。    在线客服系统及源码:zxkfym.top  什么是在线客服系统?    线客服系统是数字化工作场所的重要工具之一。它用于远程工作的员工之间的沟通和协作,从而使工作更容易、更快。尽管即时消息平台首先用.

    2022年7月19日
    15
  • jQuery+CSS3文字跑马灯特效

    jQuery+CSS3文字跑马灯特效是一款将跑马灯背景制作为3D立方体效果,文字在上面移动时,就像是文字投影到墙壁上,在转角出会改变运动方向。效果展示 http://hovertree.co

    2021年12月27日
    41
  • vscode 使用flake8和yapf[通俗易懂]

    vscode 使用flake8和yapf[通俗易懂]vscode使用flake8和yapf

    2025年11月2日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号