Java递归解决n皇后问题

题目

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

代码

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

/**
 * Created by GuanDS on 2018/9/20.
 */
public class Queen {

    public static void main(String[] args) {
        long currentTime = System.currentTimeMillis();
        int n = 8;
        Queen queen = new Queen();
        short[] cols = queen.getNewShort(n); // 初始化数组, 存放列, 不能为默认值, 设置为2*n
        queen.putQueen(new short[n][n], (short) 0, cols, cols.clone(), cols.clone());
        System.out.println(n + "皇后, 共 " + queen.list.size() + " 种情况, 用时: " + (System.currentTimeMillis() - currentTime) + "ms");
    }

    private List<short[][]> list = new ArrayList<>();

    /**
     * 递归存放皇后
     * @param queens 历史数组
     * @param c 当前第c个
     * @param cols 以存放皇后的列序
     * @param rowAddCols 行序+列序
     * @param rowSubCols 行序-列序
     */
    private void putQueen(short[][] queens, short c, short[] cols, short[] rowAddCols, short[] rowSubCols) {
        for (short i = 0; i < queens.length; i++) {
            if (isContains(cols, i)
                    || isContains(rowAddCols, (short) (c + i))
                    || isContains(rowSubCols, (short) (c - i))) {
                continue;
            }
            short[][] tqueens = copy(queens);
            short[] tcols = cols.clone();
            short[] trowAddCols = rowAddCols.clone();
            short[] trowSubCols = rowSubCols.clone();
            tqueens[c][i] = 1;
            tcols[c] = i;
            trowAddCols[c] = (short) (c + i);
            trowSubCols[c] = (short) (c - i);

            if (c >= queens.length - 1) {
                list.add(tqueens);
            } else {
                putQueen(tqueens, (short) (c + 1), tcols, trowAddCols, trowSubCols);
            }
        }
    }

    // 判断数组中是否包含某元素
    private boolean isContains(short[] array, short n) {
        for (short i : array) {
            if (i == n) {
                return true;
            }
        }
        return false;
    }

    // 初始化一个新的short数组
    private short[] getNewShort(int n) {
        short[] shorts = new short[n];
        for (int i = 0; i < n; i++) {
            shorts[i] = (short) (2 * n);
        }
        return shorts;
    }

    // copy二维short数组
    private static short[][] copy(short[][] array) {
        short[][] result = new short[array.length][array[0].length];
        for (int m = 0; m < array.length; m++) {
            for (int n = 0; n < array[0].length; n++) {
                result[m][n] = array[m][n];
            }
        }
        return result;
    }

}

扩展

执行15皇后时, 需要运行270s左右, 性能很差, 因为使用了二维数组, 及三个一位数组作为参数传递.

解决方式: 使用一个为数组做记录, 第i个元素的值表示第i行的列序, 性能提升4倍, 15皇后用时45s, 代码如下:

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

public class Queen {

    public static void main(String[] args) {
        long currentTime = System.currentTimeMillis();
        int n = 15;
        Queen queen = new Queen();
        short[] array = new short[n];
        // 初始化数组, 不能为默认值, 设置为2*n
        for (int i = 0; i < n; i++) {
            array[i] = (short) (2 * n);
        }
        queen.putQueen(array, (short) 0);
        System.out.println(n + "皇后, 共 " + queen.list.size() + " 种情况, 用时: " + (System.currentTimeMillis() - currentTime) + "ms");
    }

    private List<short[]> list = new ArrayList<>();

    /**
     * 递归存放皇后
     *
     * @param queens 历史数组
     * @param c      当前第c个
     */
    private void putQueen(short[] queens, short c) {
        for (short i = 0; i < queens.length; i++) {
            if (!checkQueen(queens, c, i)) {
                continue;
            }
            short[] tempQueens = queens.clone();
            tempQueens[c] = i;

            if (c >= queens.length - 1) {
                list.add(tempQueens);
            } else {
                putQueen(tempQueens, (short) (c + 1));
            }
        }
    }

    private boolean checkQueen(short[] queen, int c, int i) {
        for (int k = 0; k < queen.length; k++) {
            if (queen[k] != 2 * queen.length) {
                if (queen[k] == i || k - queen[k] == c - i || k + queen[k] == c + i) {
                    return false;
                }
            } else {
                break;
            }
        }
        return true;
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值