回溯算法

回溯算法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。 

来源

回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。
用回溯算法解决问题的一般步骤:
1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
3 、以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。 

基本思想

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

迷宫回溯问题

public class MiGong {
    public static void main(String[] args) {
        /*创建二维数组模拟迷宫
        * 地图*/
        int[][] map =new int[8][7];
        /*1:墙2:
        * 上下置为1*/
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        /*左右置为1*/
        for (int i = 0; i <8 ; i++) {
            map[i][0]=1;
            map[i][6]=1;
        }
        map[3][1]=1;
        map[3][2]=1;

        //输出地图
        System.out.println("地图情况=====");
        for (int i = 0; i <8 ; i++) {
            for (int j = 0; j <7 ; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }

        //使用递归回溯给小球找路
        setWay(map,1,1);
        //输出新地图,小球走过并标识的路

        System.out.println("小球走过并标识的路=====");
        for (int i = 0; i <8 ; i++) {
            for (int j = 0; j <7 ; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }



    }



    /*使用递归回溯来给小球找路
    * 1.起始位置:(1,1)
    * 如果找到map[6][5],说明通路找到
    * 2.约定:0:该点没有找过1:表示墙,2:通路可以走3:该点已经走过但是不能走通 断路不能走
    * 3.策略:行进方向()下--》右--》上--》左,走不通进行回溯*/
    /**
     *
     * @description:使用递归回溯来给小球找路
     * @params:1:地图  起始位置 2:行3:列
     * @return: 找到通路为真,否则为假
     * @author: 苏兴旺
     * @time: 2020/3/12 11:54
     */
    public static  boolean setWay(int[][]map,int i ,int j){
        if (map[6][5]==2){//通路找到
            return true;
        }else {
            if (map[i][j]==0){//如果当前点没有走过
                /*按照策略下--》右--》上--》左走*/
                map[i][j] = 2;//假定该点可以走
                if (setWay(map, i+1, j)){//向下走
                    return true;
                }else if (setWay(map, i, j+1)){//向右走
                    return true;
                }else if (setWay(map, i-1, j)){//向上
                    return true;
                }else if (setWay(map, i, j-1)){//向左
                    return true;
                }else {
                    map[i][j] = 3;//死路走不通
                    return false;
                }
            }else {//map[i][j]!=0
                /*1: 2: 3:*/
                return false;
            }
        }
    }



}

 八皇后问题

八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有 76 种方案。1854 年在柏林的象棋杂志上不同的作者发表了 40 种不同的解,后来有人用图论的方法解出 92 种结果。计算机发明后,有多种计算机语言可以编程解决此问题。八皇后问题如果用穷举法需要尝试 88 =16,777,216 种情况。每一列放一个皇后,可以放在第 1 行,第 2 行,……,直到第 8 行。穷举的时候从所有皇后都放在第 1 行的方案开始,检验皇后之间是否会相互攻击。如果会,把列 H 的皇后挪一格,验证下一个方案。移到底了就 “进位” 到列 G 的皇后挪一格,列 H 的皇后重新试过全部的 8 行。如图 1 所示,这种方法无疑是非常低效率的,因为它并不是哪里有冲突就调整哪里,而是盲目地按既定顺序枚举所有的可能方案。

回溯算法优于穷举法。将列 A 的皇后放在第一行以后,列 B 的皇后放在第一行已经发生冲突。这时候不必继续放列 C 的皇后,而是调整列 B 的皇后到第二行,继续冲突放第三行,不冲突了才开始进入列 C。如此可依次放下列 A 至 E 的皇后,如图 2 所示。将每个皇后往右边横向、斜向攻击的点位用叉标记,发现列 F 的皇后无处安身。这时回溯到列 E 的皇后,将其位置由第 4 行调整为第 8 行,进入列 F,发现皇后依然无处安身,再次回溯列 E。此时列 E 已经枚举完所有情况,回溯至列 D,将其由第 2 行移至第 7 行,再进入列 E 继续。按此算法流程最终找到如图 3 所示的解,成功在棋盘里放下了 8 个 “和平共处” 的皇后。继续找完全部的解共 92 个。
回溯算法求解八皇后问题的原则是:有冲突解决冲突,没有冲突往前走,无路可走往回退,走到最后是答案。为了加快有无冲突的判断速度,可以给每行和两个方向的每条对角线是否有皇后占据建立标志数组。放下一个新皇后做标志,回溯时挪动一个旧皇后清除标志。以下给出回溯算法解八皇后问题的编程代码实现。
public class Queue8 {
    public static void main(String[] args) {
        Queue8 queue8 = new Queue8();
        queue8.check(0);
        System.out.printf("一共有%d中解法",count);

    }






    /*定义一个max:多少个皇后*/
    int max = 8;
   static int count=0;

    /*定义一个数组放置位置的结果*/
    int[] array = new int[max];

    /*将皇后摆放的位置输出*/
    private  void print(){
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
        System.out.println();
    }


    /*放置第n个皇后*/
    private void check(int n){
        if (n ==max){//n=8 其实8个皇后已经放好
            print();
            return;
        }
        //依次放入皇后判断是否冲突
        for (int i = 0; i <max ; i++) {
            //先把当前皇后n放到该行的第1列
            array[n]=i;
            /*判断放置第n个皇后到i列时是否冲突*/
            if (judge(n)){//不冲突接着放下一个皇后开始递归
                check(n+1);
            }
            /*如果冲突,就继续arry[n]=i即将第n个皇后放置在本行的后一个位置*/

        }
    }

    /*查看当我们放置第n个皇后时:判断检查该皇后是否和之前摆放的皇后冲突*/
    private boolean judge(int n){
        for (int i = 0; i < n; i++) {
            if (array[i]==array[n] ||Math.abs(n-i)==Math.abs(array[n]-array[i])){//1.同一列2.通一斜线
               return false;
            }
        }
      return true;
    }
}
 
原文地址:https://www.cnblogs.com/sxw123/p/12806112.html