项目github地址:bitcarmanlee easy-algorithm-interview-and-practice 欢迎大家star,留言,一起学习进步
1.什么是八皇后问题
八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。(问题描述来自参考文献1)
数学王子高斯当年花费了无数心血,最后计算认为八皇后问题有76种解法,现在我们通过计算机模拟可以轻松计算出八皇后问题的真正解法有92种。能让高斯栽跟头的问题,可想而知计算的复杂性与难度。
2.解法分析
高斯将八皇后的解法推导出76种,已经算是非常厉害了。这不能怨高斯,而是八皇后的计算复杂度确实太高。 该问题的整体思路还是暴力解法 1.先列出所有可能的皇后摆放方式。 2.再检查该种摆放方式是否符合要求。
具体的执行过程如下: 先将第一个皇后放在第一行第一列,然后将第二个皇后放在第二行第一列,判断该种摆法是否符合要求。很明显这样摆不行,有两个皇后会在同一列。再将第二个皇后放在第二行第二列,这样也不行,两个皇后会在一条斜线上。将第二个皇后放在第二行第三列,这样满足当前条件。 目前已经有两个皇后满足条件,接下来放第三个,还是从第三行第一列开始放置,不满足条件再放第二列,第三列…一直到第8个皇后也能放在一个不冲突的位置,此时找到一个符合要求的解。 然后我们开始回溯,将第一个皇后放在第一行第二列,后面的就继续按上面的方式循环,一直到回溯完毕,找出所有符合条件的解为止。
参考文献2有张图,比较详细的用图描述了上面的过程,贴出来大家参考。下面图描述的是4皇后的回溯过程,原理跟8皇后是一致的。
3.代码实现
上面原理分析完毕,接下来看代码实现。
public class NQueueV2 {
public static int N = 4;
public static int[][] boards = new int[N][N];
public static int result = 0;
public static void putQueQue(int k) {
if (k == N) {
result++;
for(int row=0; row for(int col=0; col System.out.print(boards[row][col] + " "); } System.out.println(); } System.out.println(); } else { for(int i=0; i if (check(k, i)) { boards[k][i] = 1; putQueQue(k+1); boards[k][i] = 0; } } } } public static boolean check(int row, int column) { for(int i=0; i if (boards[i][column] == 1) { return false; } } for(int m=row-1, n=column-1; m>=0 && n >= 0; m--, n--) { if (boards[m][n] == 1) { return false; } } for(int m=row-1, n=column+1; m>=0 && n if (boards[m][n] == 1) { return false; } } return true; } public static void main(String[] args) { putQueQue(0); System.out.println("result is: " + result); } } 代码输出结果: 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 result is: 2 上面的代码,为了方便输出最后结果,模拟的是4皇后,8皇后只需要将N改为8即可。 board二维数组,模拟的是棋盘,初始状态均为0,表示该位置没有放置皇后。如果该位置设为1,表明该位置有皇后。 k表示放置的皇后个数。如果k==N,说明N个皇后都被放置完毕,此时得到一个有效解。 for(int row=0; row for(int col=0; col System.out.print(boards[row][col] + " "); } System.out.println(); } 该部分代码输出有效解的状态,数组为1的位置表示该位置放置了皇后。 else部分代码,i表示的是第i列,check(k, i)方法表示的是在(k, i)这个位置放置皇后是否合法 for(int i=0; i if (boards[i][column] == 1) { return false; } } 这部分代码,表示两个皇后不能放置在同一列。 for(int m=row-1, n=column-1; m>=0 && n >= 0; m--, n--) { if (boards[m][n] == 1) { return false; } } 这部分代码,表示该皇后的左斜线部分不能有皇后。 for(int m=row-1, n=column+1; m>=0 && n if (boards[m][n] == 1) { return false; } } 这部分代码,表示该皇后的右斜线部分不能有皇后。 如果该位置能放置皇后,则将棋盘中该位置置为1,接下来往下一行放皇后putQueQue(k+1)。同时,在回溯的时候,需要将该位置置为0。 4.套路总结 所以看上去很复杂的八皇后问题,对着图,再对着代码分析,是不是感觉没那么难了? 本质还是经典回溯算法的使用,跟前面我们讲到的排列组合类似。当了解完整个过程以后,回溯的难度甚至比排列组合还小! 史上最全排列组合算法详解以及套路总结一文搞定 参考文献 1.https://zh.wikipedia.org/wiki/%E5%85%AB%E7%9A%87%E5%90%8E%E9%97%AE%E9%A2%98 2.https://juejin.cn/post/6844903798926753805