回溯算法之八皇后问题
一、什么是回溯算法?
我们肯定都玩过迷宫游戏吧,比较复杂的迷宫,肯定是不可能第一遍就直接过了,只能一步一步地进行尝试。当走到一个死胡同时,只能退回到上一个分岔口进行重新选择。
数独游戏也是这样的,对于一个不确定的方格,我们就会先将这个方格可能出现的问题记录下来,一个一个地尝试,直到得到正确解。有着“通用解”称呼
所以,回溯算法就是类似于枚举的算法,将这一步的所以可能性一个一个地进行尝试。上边迷宫中的分岔口和数独中的可能出现多个数字的方格就是“回溯点”
二、有什么优缺点?
- 因为要对每一个点的可能情况都进行枚举测试,所以效率特别低,比如后边下边例子中的“八皇后问题”中,总共要进行15000次左右的运算,虽然对于计算机来说是很快的,但是更加复杂的问题,可能会更多
- 它可是有着“通用解”称呼,基本上大多数的这类问题都可以用此方法解决。

三、经典的“八皇后”问题
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
使用Java来解决此问题主要有以下步骤:
- 设计一个数据模型来解决此问题,
1
| private int result[] = new int[8];
|
首先想到的就是一个二维数组,但是我在学习的时候,看到老师用了一个长度为8的一维数组就解决了,思想如下:
* 使用int[8]来表示一个正确解,也就是说,这个数组中的8个数组分别表示第一行、第二行、第三行……直到第八行中正确的棋子的位置,比如其中一个解如下图,它所表示的结果就是:{1,5,8,6,3,7,2,4}

- 找到“回溯点”
- 第一种回溯情况:
这个问题的解决方案就是从每一行的每一个点开始摆放棋子,如果这个棋子可以话这里,就继续放置下一行。如果不能放置在这里,就往后移,直到这一行的8个位置全都不能的话,就往上回溯一行,将上一行的棋子往右移一行,再继续放置这一行,如果上一行全部放了个遍还是不行,就再往上回溯一行,就这样继续。。。
- 第二种回溯情况:
因为要找到所有的问题,第二个回溯情况就是当一种摆法摆放完成后。从最后一行进行回溯,比如上边的解中{1,5,8,6,3,7,2,4},当最后一个4摆放完成后,就将最后一个放在5这个位置上,不行的话就放6,7,8,如果全都不行,就将上一行中的2放在3位置上,再从第八行的1,2,3…这样放
- 写出这个判断这个回溯点的条件方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
private boolean check(int n) { timeCount ; for (int i = 0; i < n; i ) {
if (result[i] == result[n] || Math.abs(n - i) == Math.abs(result[n] - result[i])) { return false; } } return true; }
|
4.开始写放置棋子的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public void calResult(int row) {
if (row > 7) { sum ; printResult(); return; }
for (int i = 0; i < max; i ) { result[row] = i; if (check(row)) { calResult(row 1); } } }
|
最后的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| package cn.lyn4ever.structure.excerise;
public class EightQueenDemo {
private int max = 8; private int result[] = new int[max]; private int sum = 0; private int timeCount = 0;
private boolean check(int n) { timeCount ; for (int i = 0; i < n; i ) {
if (result[i] == result[n] || Math.abs(n - i) == Math.abs(result[n] - result[i])) { return false; } } return true; }
private void printResult() { for (int i = 0, length = result.length; i < length; i ) { System.out.print(result[i] " "); } System.out.println(); }
public void calResult(int row) {
if (row > 7) { sum ; printResult(); return; }
for (int i = 0; i < max; i ) { result[row] = i; if (check(row)) { calResult(row 1); } } }
public static void main(String[] args) {
EightQueenDemo eightQueenDemo = new EightQueenDemo(); eightQueenDemo.calResult(0); System.out.printf("八皇后问题的解法一共有%d", eightQueenDemo.sum); System.out.printf("一共进行了%d次运算", eightQueenDemo.timeCount); }
}
|
最后的运行结果如下:
