八皇后问题是一个以国际象棋为背景的问题:如何能够在 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 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 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;
}
}