一、基本概念
回溯算法实际上是一个枚举的搜索尝试过程,主要在搜索尝试过程中寻求问题的解,当发现满足求解条件时,则将路径存储,否则不做任何处理。
二、设计思想
回溯思想:穷举搜索
回溯设计:(1)域:设置一个数组arr用于存放可达路径,设置一个数组List<arr>用于存放所有可达路径
(2)问题转化:问题转化为求解可达路径下一步如何走的问题
(3)方法逻辑:给出可达路径的终止条件,满足条件时存储路径,否则将大问题分割成多个规模较小的相同问题,递归求解。
三、问题分析
下面以八皇后问题进行说明,原题表述如下:
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
算法选型:
八皇后问题实质是对可达路径的穷举求解,因而应该选用回溯算法
题目分析:
由于皇后不能处于同一行,因此按行来规定皇后,第一行放第一个皇后,第二行放第二行皇后,直到所有行放完;在上述过程中涉及到两点,首先每行的一个皇后放置在什么位置,这是不确定的需要穷举,但必须满足与前面皇后不能同一列,也不能同一对角线
算法逻辑:
(1)域:array用于存放八皇后的正确摆放方式,storage用于存储所有的摆放方式
(2)问题转化:问题转化为求解第n个皇后放置在什么位置的问题,即:void placeQueue(int n);
(3)终止条件:当摆放第8个皇后时即为终止信号,说明当前路径符合要求即为存储
(4)子问题递归:第n个皇后有多种方法可以放在任意位置,因为需要for穷举,当第n个皇后放置后如果与前面放置的n-1个皇后满足上述限制条件,则继续放置n-1个皇后,与前述完全一致,递归即可
代码示意如下所示:
public class Example { private static ArrayList<ArrayList<Integer>> storage = Lists.newArrayList(); /** * 总皇后数,此时设置为8皇后在8X8棋盘的摆放 **/ private static int max = 8; /** * 存放八皇后的摆放方式,第一个皇后摆在array[0]列,第二个摆在array[1]列,..... **/ private static int[] array = new int[max]; /** * @param n 当前是第几个皇后(从0开始计算) **/ private static void placeQueue(int n) { // 一行摆放完毕 if (n == max) { ArrayList<Integer> arrayList = Lists.newArrayList(); for (int i = 0; i < max; i++) { arrayList.add(array[i]); } storage.add(arrayList); return; } //从第一列开始放值,然后判断是否和本行本列本斜线有冲突,如果OK,就进入下一行的逻辑 for (int i = 0; i < max; i++) { array[n] = i; // 判断是否符合八皇后条件,符合则摆放下一个皇后,否则回退继续尝试摆放 if (judgeLegal(n)) { placeQueue(n + 1); } } } /** * @param n 摆放的当前皇后的编号, array[n]为要当前摆放的皇后的位置,array[0~n-1]为前n-1已经摆放的皇后位置 * @func 判断是否符合八皇后条件 * 分析: 假设一个皇后所在位置为n列,位置为arr[n],坐标为(n, arr[n]), 另一个皇后的坐标为(i, arr[i]), * 如果两个皇后在一条对角线上, 画图根据数学分析可知,则必有Math.abs(n-i) == Math.abs(arr[n], arr[i]); * 如果两个皇后在不同行的同一位置则必有arr[n] == arr[i] **/ private static boolean judgeLegal(int n) { for (int i = 0; i < n; i++) { if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) { return false; } } return true; } public static void main(String[] args) { placeQueue(0); for (int i = 0; i < storage.size(); i++) { System.out.println(storage.get(i)); } } }