引言:
寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解。理论上,当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方法是可行的。不过,在实际应用中,很少使用这种方法,因为候选解的数量通常都非常大(比如指数级,甚至是大数阶乘),即便采用最快的计算机也只能解决规模很小的问题。对候选解进行系统检查的方法有多种,其中回溯和分枝定界法是比较常用的两种方法。按照这两种方法对候选解进行系统检查通常会使问题的求解时间大大减少(无论对于最坏情形还是对于一般情形)。事实上,这些方法可以使我们避免对很大的候选解集合进行检查,同时能够保证算法运行结束时可以找到所需要的解。因此,这些方法通常能够用来求解规模很大的问题。
算法思想:
回溯(backtracking)是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。
下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图(迷宫问题)或树(N皇后问题)。
一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。
回溯方法的步骤如下:
1) 定义一个解空间,它包含问题的解。
2) 用适于搜索的方式组织该空间。
3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。
回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。
八皇后问题:
public class Empress {
static int n = 8;
static int num = 0;
static int[] a = new int[9];
public static void main(String[] args) {
// TODO Auto-generated method stub
Empress s = new Empress();
s.findQueen(1);
System.out.println(num);
}
public void findQueen(int t) {//t表示第几行
if(t > n) {
num++;//如果大于8,则找到一个解,输出
print(a);
} else {
for(int i = 1; i <= n;i++) {//下一个节点有n个选择
a[t] = i;//a[t]表示第几列
if(isValid(t)) {//符合皇后规则,继续寻找下一个节点。不符合,则寻找下一列.
findQueen(t+1);
}
}
}
}
public void print(int[] a) {//打印
for(int i = 1; i < a.length;i++ ) {
System.out.print("皇后 " + i + "位于" + "第" + i + "行" + a[i] + "列 ");
}
System.out.println("-----------------");
}
public boolean isValid(int t){//判断是否合法,即两个皇后既不能在同一行同一列同一斜线。
for(int i = 1;i < t;i++) {
if(Math.abs(t-i) == Math.abs(a[t]-a[i]) || a[i] == a[t]){
return false;
}
}
return true;
}
}