通过这节课,我们将学到:八皇后摆放问题、走迷宫的解法
回溯(Back Tracking)
这个英文单词可以的,”回去追踪”,回溯
回溯可以理解为:通过选择不同的岔口来通往目的地
每一步选择一条路出发,能进则进,不能进则退回上一步(回溯),换一条路再试
树、图的深度优先搜索就是典型的回溯应用
可以看出,回溯适合使用递归来解决。
知识的串联性,递归->回溯
八皇后问题(Eight Queens)
在一个8x8的国际象棋上,摆放八个皇后,使其不能相互攻击:
如果任意两个皇后处于同一行、同一列、同一斜线,则会发生攻击。
也就是,任意两个皇后,不能处于同一行、同一列、同一斜线
问:有多少种摆法?
八皇后解题思路
方法一:暴力法
从64个格子中,任意选出8个摆放,选出符合条件的摆法。
方法二:优化后的暴力法
每一行只能放一个
方法三:回溯+剪枝
剪枝(Pruning)
package com.yz;
public class Main {
public static void main(String[] args) {
new Main().placeQueens(4);
}
/**
* cols[row] = col
* 第row行存放的皇后在第col列
* */
int[] cols;
//有多少种摆法
int ways;
/**摆放n皇后*/
void placeQueens(int n)
{
if(n < 1) return;
cols = new int[n];
place(0);
System.out.println(n + "皇后一共有" + ways + "种摆法");
}
/**从第row行开始摆放皇后*/
void place(int row)
{
if (row == cols.length) {
ways++;
show();
return;
}
for (int col = 0; col < cols.length; col++) {
if (isValid(row, col)) {
cols[row] = col;
place(row + 1);
}
}
}
/**
* 判断第row行,第col列能否摆放皇后
* */
boolean isValid(int row, int col)
{
for (int i = 0; i < row; i++) {
//第col列已经有皇后
if (cols[i] == col) return false;
//判断斜线
if(row - i == Math.abs(col - cols[i])) return false;
}
return true;
}
/**
* 打印
* */
void show()
{
for (int row = 0; row < cols.length; row++) {
for (int col = 0; col < cols.length; col++) {
if (cols[row] == col) {
System.out.print("1 ");
}else{
System.out.print("0 ");
}
}
System.out.println();
}
System.out.println("------------");
}
}
优化
在判断某一位置是否可以放皇后的时候,我们需要遍历之前所有row的情况,找出该点相关的列与该列是否相同。
我们其实只需要知道某一点的某列上是否存在皇后即可,并不需要知道已存在皇后的具体位置。
也就是int[] cols;可以修改为boolean[] cols;
同理,可以使用一个数组表示某一格子的斜线是否有皇后。
内存空间的优化
但是,这种做法,只能求出有多少种摆放方法,并不能具体打印出如何摆放。
因此,不做代码展示,以上面代码为准。
还可以通过位运算优化代码(不做阐述)