/**
* 回溯法求解 N 皇后问题
* @author haolloyin
*/
public class N_Queens {
// 皇后的个数
private int queensNum = 5;
// column[i] = j 表示第 i 列的第 j 行放置一个皇后
private int[] queens = new int[queensNum + 1];
// rowExists[i] = true 表示第 i 行有皇后
private boolean[] rowExists = new boolean[queensNum + 1];
// a[i] = true 表示右高左低的第 i 条斜线有皇后
private boolean[] a = new boolean[queensNum * 2];
// b[i] = true 表示左高右低的第 i 条斜线有皇后
private boolean[] b = new boolean[queensNum * 2];
// 初始化变量
private void init() {
for (int i = 0; i < queensNum + 1; i++) {
rowExists[i] = false;
}
for(int i = 0; i < queensNum * 2; i++) {
a[i] = b[i] = false;
}
}
// 判断该位置是否已经存在一个皇后,存在则返回 true
private boolean isExists(int row, int col) {
return (rowExists[row] || a[row + col - 1] || b[queensNum + col - row]);
}
// 主方法:测试放置皇后
public void testing(int column) {
// 遍历每一行
for (int row = 1; row < queensNum + 1; row++) {
// 如果第 row 行第 column 列可以放置皇后
if (!isExists(row, column)) {
// 设置第 row 行第 column 列有皇后
queens[column] = row;
// 设置以第 row 行第 column 列为交叉点的斜线不可放置皇后
rowExists[row] = a[row + column - 1] = b[queensNum + column - row] = true;
// 全部尝试过,打印
if(column == queensNum) {
for(int col = 1; col <= queensNum; col++) {
System.out.print("("+col + "," + queens[col] + ") ");
}
System.out.println();
}else {
// 放置下一列的皇后
testing(column + 1);
}
// 撤销上一步所放置的皇后,即回溯
rowExists[row] = a[row + column - 1] = b[queensNum + column - row] = false;
}
}
}
// 测试
public static void main(String[] args) {
N_Queens queen = new N_Queens();
queen.init();
// 从第 1 列开始求解
queen.testing(1);
}
}
当 N = 5 时,求解结果如下(注:
横坐标为 列数, 纵坐标为 行数
):
(1,1) (2,3) (3,5) (4,2) (5,4)
(1,1) (2,4) (3,2) (4,5) (5,3)
(1,2) (2,4) (3,1) (4,3) (5,5)
(1,2) (2,5) (3,3) (4,1) (5,4)
(1,3) (2,1) (3,4) (4,2) (5,5)
(1,3) (2,5) (3,2) (4,4) (5,1)
(1,4) (2,1) (3,3) (4,5) (5,2)
(1,4) (2,2) (3,5) (4,3) (5,1)
(1,5) (2,2) (3,4) (4,1) (5,3)
(1,5) (2,3) (3,1) (4,4) (5,2)
当 N = 4 时,求解结果如下:
(1,2) (2,4) (3,1) (4,3)
(1,3) (2,1) (3,4) (4,2)
有时间的话将输出的结果打印为直观一点的符号形式或界面形式更好。
小结:
1、根据问题选择恰当的数据结构非常重要,就像上面 a 、b 标志数组来表示每一条斜线的编号顺序以及方向都相当重要。看书的时候也是费了些时间来理解的,呼…另外,queens [col] = row 数组只是用了一维而不是二维来表示纵横交错的方格棋盘上特定位置是否有皇后也是比较经济而有意思的。
2、正确运用、组织所确定的数据结构到算法的实现逻辑中也是很重要的,就像代码中的 isExists(int row, int col) 方法内的 (rowExists[row] || a[row + col - 1] || b[queensNum + col - row]) 就是很明确的理解了尝试放置皇后的位置的 x ,y 坐标与斜线之间的数值关系,才使得算法得以正确执行。当然,对于斜线的编号、顺序也是相当重要的。