序言
经典游戏:八皇后问题
游戏规则:8个皇后在矩阵图里排列,在每个皇后的同一行同一列上,以及同一斜线上不能有其他皇后,否则就会冲突。其示意图如下:
Java代码实现(带注释)如下:
import java.util.*;
public class n51 {
// 最外层的list生成
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<>();
int[] queens = new int[n];
Arrays.fill(queens, -1);
Set<Integer> columns = new HashSet<>();
Set<Integer> diagonals1 = new HashSet<>();
Set<Integer> diagonals2 = new HashSet<>();
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
// 回溯算法实现 —— 定义方法 backtrack()
private static void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
// 当行数等于输入个数 n 的时候生成 n皇后矩阵图
if (row == n) {
List<String> board = generateBoard(queens, n);
solutions.add(board);
} else {
for (int i = 0; i < n; i++) {
// 如果列包含 i,则继续返回上面的for循环,否则才进入下一个if判断语句里面
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
// 如果都不包含上述元素,并添加这些元素到 queens 数组里面
queens[row] = i;
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
// 回溯调用自己 —— 回溯算法的核心思想,和递归一样,回调自己,如果不调用自己,就不叫递归了
backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
// 当行数 row 等于输入个数 n 的时候生成 n皇后矩阵图后清除上一轮的记录(分别remove掉上一次记录的元素),
// 并再次进入上面的for循环,开始排列 n皇后
queens[row] = -1;
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
}
}
/**
* 打印的效果如下
* .Q..
* ...Q
* Q...
* ..Q.
*/
// 生成 n皇后矩阵图
public static List<String> generateBoard(int[] queens, int n) {
List<String> board = new ArrayList<>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[i]] = 'Q';
board.add(new String(row));
}
return board;
}
// /**
// * 打印的效果如下
// * [., Q, ., .]
// * [., ., ., Q]
// * [Q, ., ., .]
// * [., ., Q, .]
// * */
// public static List<String> generateBoard(int[] queens, int n){
// List<String> board = new ArrayList<>();
// for (int i = 0; i < n; i++) {
// String[] row = new String[n];
// Arrays.fill(row, ".");
// row[queens[i]] = "Q";
// board.add(new String(Arrays.toString(row)));
// }
// return board;
// }
// 测试
public static void main(String[] args) {
// 因为是非static方法,所以要先new一个类
n51 list1 = new n51();
// 然后通过 变量.方法名 来调用
int n;
List<List<String>> list = list1.solveNQueens(n = 5);
// System.out.println("一行内打印全部" + list);
int i; // 在for外面定义变量,为了在最后打印总的结果
System.out.println("=======换行打印内部的每一个list=======");
// 先循环外面一层的list
for (i = 0; i < list.size(); i++) {
// list.get(i)为里面一层的list,
List<String> item = list.get(i);
// System.out.println("=======换行打印内部的每一个list的每一个元素,形成矩阵图=======");
// // 拿到里面的list之后,给它赋值一个变量,名为item,并再次对这个list进行循环
// 增强for循环写法
// for (String str : item) {
// System.out.println(str);
// }
// 普通for循环写法如下
// for (int j = 0; j < item.size(); j++) {
// String str = item.get(j);
// System.out.println(str);
// }
System.out.println("内部的第" + i + "个list:" + item);
}
System.out.println("\n" + n + "个皇后," + "共" + i + "种排列方法");
}
}
无注释完整版如下:
import java.util.*;
public class n51 {
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<>();
int[] queens = new int[n];
Arrays.fill(queens, -1);
Set<Integer> columns = new HashSet<>();
Set<Integer> diagonals1 = new HashSet<>();
Set<Integer> diagonals2 = new HashSet<>();
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
private static void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
List<String> board = generateBoard(queens, n);
solutions.add(board);
} else {
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
queens[row] = i;
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
queens[row] = -1;
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
}
}
public static List<String> generateBoard(int[] queens, int n) {
List<String> board = new ArrayList<>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[i]] = 'Q';
board.add(new String(row));
}
return board;
}
public static void main(String[] args) {
n51 list1 = new n51();
int n;
List<List<String>> list = list1.solveNQueens(n = 5);
int i;
System.out.println("=======换行打印内部的每一个list=======");
for (i = 0; i < list.size(); i++) {
List<String> item = list.get(i);
for (String str : item) {
System.out.println(str);
}
System.out.println("内部的第" + i + "个list:" + item);
}
System.out.println("\n" + n + "个皇后," + "共" + i + "种排列方法");
}
}