五大常用算法一(回溯,随机化,动态规划)
作者:fredal 来源:简书
链接:https://www.jianshu.com/p/dec9a453573f
回溯算法
回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并以此慢慢地扩大问题规模,迭代地逼近最终问题的解。这种迭代类似于穷举并且是试探性的,因为当目前的可能答案被测试出不可能可以获得最终解时,则撤销当前的这一步求解过程,回溯到上一步寻找其他求解路径。
为了能够撤销当前的求解过程,必须保存上一步以来的求解路径,这一点相当重要。
-
八皇后问题
8皇后问题是其经典的问题,描述为:在一个 8x8的国际象棋棋盘中,怎样放置8个皇后才能使8个皇 后之间不会互相有威胁而共同存在于棋局中,即在8x8个格子的棋盘中没有任何两个皇后是在同一行、同一列、同一斜线上。
思路:用回溯法,最容易想到的方法就是有序地从第 1 列的第 1 行开始,尝试放上一个皇后,然后再尝试第 2 列的第几行能够放上一个皇后,如果第 2 列也放置成功,那么就继续放置第 3 列,如果此时第 3 列没有一行可以放置一个皇后,说明目前为止的尝试是无效的(即不可能得到最终解),那么此时就应该回溯到上一步(即第 2 步),将上一步(第 2 步)所放置的皇后的位置再重新取走放在另一个符合要求的地方…如此尝试性地遍历加上回溯,就可以慢慢地逼近最终解。
我们用代码模拟该步骤,比较简单的是使用递归方法
package com.fredal.structure;
public class NQueen {
static int index=0;
private static void show(int[] queen){
for(int i=0;i<queen.length;i++){
System.out.print(queen[i]+" ");
}
System.out.println();
index++;
}
//k表示层数 i表示列
public static void process(int[] queen,int k){
if(k==8){//找到解了
show(queen);
return;
}
for(int i=0;i<8;i++){
if(!check(queen, k, i))
continue;
else {
queen[k]=i;//放置皇后k
process(queen, k+1);//放置皇后k+1
queen[k]=-1;//更好地表示回溯 会被覆盖的
}
}
}
//新皇后放入的位置
private static boolean check(int[] queen,int row,int col){
for(int i=0;i<row;i++) if(queen[i]==col) return false;//垂直检测
for(int i=0;i<row;i++) if(row-i==Math.abs(col-queen[i])) return false;//对角线检测
return true;
}
public static void main(String[] args) {
int[] queen=new int[8];
process(queen,0);
System.out.println("共有"+index+"种解法");
}
}
可以得到共有92种解法,我们注意到使用递归方法回溯的思想体现的不那么明显,因为递归是内部自动回溯的,效率也比非递归低一点的,所以我们又使用循环来模拟
关键在于如何回溯以及回溯的时机,冲突了需要回溯,同时找到一个解之后仍然需要回溯,这点值得注意!
package com.fredal.structure;
public class NQueen {
static int index=0;
private static void show(int[] queen){
for(int i=0;i<queen.length;i++){
System.out.print(queen[i]+" ");
}
System.out.println();
index++;
}
private static void init(int[] queen){
for(int i=0;i<queen.length;i++){
queen[i]=Integer.MAX_VALUE;
}
}
//k表示层数,i表示列数
public static void processS(int[] queen){
int k=0,i=0;
init(queen);//初始化数组
while(k<8){
while(i<8){
if(check(queen, k, i)){
queen[k]=i;//放置皇后k
i=0;//探测下一行 将i清0 从下一行的第0列开始探测
break;
}else{
++i;//探测下一列
}
}
if(queen[k]==Integer.MAX_VALUE){//第k行没有找到可以放皇后的地方
if(k==0) break;//回溯到第一行还没有 就终止
else{
--k;//回到上一行
i=queen[k]+1;//把上一行皇后的位置往后一列
queen[k]=Integer.MAX_VALUE;
continue;//清楚位置 重新探测
}
}
if(k==7) {//找到解了
show(queen);
i=queen[k]+1;//把最后一行皇后的位置往后一列
queen[k]=Integer.MAX_VALUE;
continue;//清楚位置 重新探测
}
++k;//探测下一行
}
}
//新皇后放入的位置
private static boolean check(int[] queen,int row,int col){
for(
