算法八皇后问题

              ## 关于算法八皇后的解决##
  1. 八皇后算法的介绍:
    八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种方法可以解决此问题。但是百度上可以见得到几乎都是什么回溯算法,这个算法确实很是麻烦,觉得死记住这个算法没有什么必要性,而今天我给大家介绍确实用另一种编程思想就是数据抽象、过程抽象的思路来解决这个问题,这是我第一次写博客,可能会有错误,请大家及时纠正。

  2. 具体步骤:
    首先八皇后问题不得不说是一道算法比较有意思的一道题,因为它的情况有很多,而恰恰这道题正是阿里巴巴p5考p6的一道经典的测试题目,既然阿里都这样注重这道难题,那就足以见得阿里对算法的重视

/**
 *  八皇后问题就是8*8的棋盘放置自己的8个皇后,要求任意
 *  两个皇后不能在同一行、同一列、任意的斜线位置
 * 首先建立一个测试用例,建立测试的模型:
 * 1、构造棋盘模型
 * 2、遍历
 *
 */
public class TestEightQueuen {
    @Test
    public void testQueuen(){
        List<Graph> graphs =  new EighthQueuen(8, 8).chose();
        for (Graph graph :graphs){
            System.out.println(graph.toString());
        }
       assertEquals(92, graphs.size());
       System.out.println(graphs.size());



    }
}
 1)、首先我们要采取测试用例进行驱动开发,首先我们建立一个棋盘的模型,把棋盘上的数据都抽像,那么就应该想着用list集合来装这些数据,所以我们抽象出来一个Gragh类这个类实际上构造棋盘上的数据,比如行、列和摆放的位置等,之后我就会想着去遍历这个Gragh类吧,但是我们还没有做怎么去选择位置添加到这个棋盘上,这个时候就需要一个EighthQueuen类了,遍历之后则进行我们已经做好的数据信息,但是现在我们仅仅是做了一个数据模型抽象的工作。下面则是逐个的进行逻辑分解和数据抽象,我们先进行逐个类的分解组合吧
 2)、首先一个最最重要的一个类就是关于皇后摆放位置情况的选择。首先由三种摆放的情况:选择的、没有选择、还有就是位置有冲突的(就是不满足题中说的两个皇后不再同一行、同一列或者是同一个斜线),所以用枚举的方式记录三种状态
public class Position {
    int rowIndex;
    int columIndex;

    enum Status{
        chosen,
        conflict,
        empty,
    }

    Status status;

    public int getStatusValue(){
        if (status == Status.chosen){
            return 1;
        }
        else{
            return 0;
        }
    }

    public Position(int rowIndex, int columIndex) {
        this.rowIndex = rowIndex;
        this.columIndex = columIndex;
        this.status = Status.empty;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "(" + rowIndex + "," + columIndex + ")";
    }

    public boolean canBeChosen(){
        return status == Status.empty;
    }
}

3)、对于横竖的棋盘位置我们把他当成一个数组去存放,这个时候就在需要一个类来单独的存放,刚开始的时候要初始化一个皇后所在位置的行和列,我们在创建一个带有位置position的一种数组集合来进行存储数据

public class DoubleArray {
    int colum = 0;
    int row = 0;
    List<List<Position>> doubleList = new ArrayList<List<Position>>();

    /**
     * 创建二维数组,私有化构造器,按照列的方式去遍历,一边遍历一遍去把含有状态位置的数据集合添加到集合中来
     * @param colum
     * @param row
     */
    public DoubleArray(int row, int colum) {
        this.colum = colum;
        this.row = row;
        for (int i = 0; i < row; i++){
            doubleList.add(createList(i));
        }
    }

    /**
     * 得到符合条件的位置数据集合,除去越界的情况,如果越界的话则会返回一个空值
     * @param rowIndex
     * @return
     */
    public List<Position> getList(int rowIndex) {
        if (rowIndex < doubleList.size()){
            return doubleList.get(rowIndex);
        }
        return null;
    }
//提前设置含有行列以及带有状态的position
    public void set(int rowPos, int columPos, Position.Status status){
        if (rowPos < row && columPos < colum){
            Position position = getList(rowPos).get(columPos);
            position.setStatus(status);
        }
    }
//含有符合行列标准的位置
    public Position get(int rowPos, int columPos){
        return getList(rowPos).get(columPos);
    }


    private List<Position> createList(int row){
        List<Position> list = new ArrayList<Position>();
        for (int j = 0; j < colum; j++){
            list.add(new Position(row,j));
        }
        return list;
    }
//创建含有位置数据的集合并进行带有符合状态的皇后进行遍历
    public DoubleArray clone(){
        DoubleArray array = new DoubleArray(this.row, this.colum);
        for (int i = 0; i < this.row; i++){
            for (int j = 0; j < this.colum; j++){
                array.set(i, j, this.get(i, j).status);
            }
        }
        return array;
    }
//tostring打印出来已经状态已经符合的位置坐标
    @Override
    public String toString() {
        String result = new String();
        for (int i = 0; i < this.row; i++){
            for (int j = 0; j < this.colum; j++){
                result += get(i, j).getStatusValue() + " ";
            }
            result += '\n';
        }
        return result;
    }
}

4).则是冲突域的解决问题,首先我们来了解一下什么冲突域的概念吧?在以太网中,如果某个CSMA/CD网络上的两台计算机在同时通信时会发生冲突,那么这个CSMA/CD网络就是一个冲突域(collision domain)。如果以太网中的各个网段以集线器连接,因为不能避免冲突,所以它们仍然是一个冲突域。就好比两个人同时抢占一个座位这就是冲突域的概念,那么在八皇后的问题中怎么会涉及到冲突域呢? 因为两个皇后会在我们自己设置集合添加的位置的时候有的时候会抢占一个位置,所以用这个冲突域来排除。

class ConflictSetter{

    DoubleArray doubleArray;
    int maxRow;
    int maxColum;

    public ConflictSetter(int maxRow, int maxColum, DoubleArray doubleArray) {
        this.maxRow = maxRow;
        this.maxColum = maxColum;
        this.doubleArray = doubleArray;
    }

    /**
     * 设置节点覆盖的冲突域
     * @param rowPos
     * @param columPos
     */
    public void occupyConfilct(int rowPos, int columPos) {
        occupyColum(columPos);
        occupyRow(rowPos);
        occupyDiagonal(rowPos, columPos);
    }

    /**
     * 设置斜线
     * @param rowPos
     * @param columPos
     */
    private void occupyDiagonal(int rowPos, int columPos) {
        setConflictUpRight(rowPos, columPos);
        setConflictDownRight(rowPos, columPos);
    }

    private void occupyRow(int rowIndex) {
        for (int i = 0; i < maxColum; i++){
            doubleArray.set(rowIndex, i, Position.Status.conflict);
        }
    }

    private void occupyColum(int columIndex) {
        for (int i = 0; i < maxRow; i++){
            doubleArray.set(i, columIndex, Position.Status.conflict);
        }
    }

    /**
     * ++
     * @param row
     * @param colum
     */
    void setConflictUpRight(int row, int colum){
        while (validColum(colum) && validRow(row)){
            doubleArray.set(row, colum, Position.Status.conflict);
            colum = increaseColum(colum);
            row = increaseRow(row);
        }
    }

    void setConflictDownRight(int row, int colum){
        while (validColum(colum) && validRow(row)){
            doubleArray.set(row, colum, Position.Status.conflict);
            colum = drecreaseColum(colum);
            row = increaseRow(row);
        }
    }


    boolean validRow(int row){
        return row >= 0 && row < maxRow;
    }

    boolean validColum(int colum){
        return colum >= 0 && colum < maxColum;
    }

    int increaseRow(int row){
        return (++row);
    }

    int increaseColum(int colum){
        return (++colum);
    }

    int drecreaseColum(int colum){
        return (--colum);
    }

}

5)棋盘类来摆放八皇后的位置,利用行列以及位置的数据集合还有冲突域进行设置

public class Graph {

    private int row;
    private int colum;
    DoubleArray doubleArray;
    ConflictSetter setter;

    public Graph(int row, int colum) {
        this.row = row;
        this.colum = colum;
        this.doubleArray = new DoubleArray(row, colum);
        this.setter = new ConflictSetter(row, colum, this.doubleArray);
    }

    public Graph() {

    }

    public void setRow(int row) {
        this.row = row;
    }

    public void setColum(int colum) {
        this.colum = colum;
    }

    /**
     * 占用一个位置
     * @param rowPos
     * @param columPos
     */
    public void chose(int rowPos, int columPos){
        setter.occupyConfilct(rowPos, columPos);
        chosePosition(rowPos, columPos);
    }

    private void chosePosition(int rowPos, int columPos) {
        doubleArray.set(rowPos, columPos, Position.Status.chosen);
    }

    /**
     * 获取当前行可以设置position列表
     * @param row
     * @return
     */
    List<Position> getOptByRow(int row){
        List<Position> optionList = new ArrayList<Position>();
        List<Position> list = doubleArray.getList(row);
        for (Position position : list){
            if (position.canBeChosen()){
                optionList.add(position);
            }
        }
        return optionList;
    }

    public Graph clone(){
        Graph graph = new Graph();
        graph.setColum(this.colum);
        graph.setRow(this.row);
        graph.doubleArray = this.doubleArray.clone();
        graph.setter = new ConflictSetter(this.row, this.colum, graph.doubleArray);
        return graph;
    }

    @Override
    public String toString() {
        return doubleArray.toString();
    }
}

6).这个类主要是进行皇后在不冲突的情况下进行遍历在棋盘的数据集合中

class EighthQueuen{
    int row  = 0;
    int colum = 0;

    public EighthQueuen(int row, int colum) {
        this.row = row;
        this.colum = colum;
    }

    public List<Graph> chose(){
        Graph graph = new Graph(row, colum);
        return chose(0, graph);
    }

    public List<Graph> chose(int row, Graph graph){
        List<Graph> graphs = new ArrayList<Graph>();
        List<Position> optList = graph.getOptByRow(row);
        for (int rowIndex = 0; rowIndex < optList.size(); rowIndex++){
            Graph newGraph = graph.clone();
            newGraph.chose(row, optList.get(rowIndex).columIndex);
            if (row == colum - 1){
                graphs.add(newGraph);
            }else{
                List<Graph> tempGraph = chose(row+1, newGraph);
                if (!tempGraph.isEmpty()){
                    graphs.addAll(tempGraph);
                }
            }
        }
        return graphs;
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值