深度优先搜索算法小总结

1:算法描述

2:应用举例

2.1:迷宫问题

  迷宫问题,是最能体现深度优先搜索的思想情况之一了,我们先分析以下迷宫问题,迷宫如下图;

图2.1.1

首先,我们顶下搜索策略,我们按照左下右上的方法进行搜索。会有以下情况。

 

图2.1.2

  当一直向左,走到最左边的时候,这个时候该位置左上右下所有位置都已经没办法走了,只能退出当前位置(退出本轮递归),返回如下:

 

图2.1.3

  退回如上图2.1.3中位置,发现该位置的左上右下已经也已经没法继续走了,所以依然退出本轮,回到上次的位置。如下图所

 

图2.1.4

  由于该位置,左边已经走过了,无法走了,所以只能下走,(因为上面走过的路径,要被标记为走过了,无法成功的,不能再走了)。

 

 

图2.1.5

代码如下:

/*
使用深度优先搜索算法解决迷宫的问题
 */
class MiGong {
    //迷宫array,1代表出口。
    public int[][] miGongArray =new int[][]{
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,1}
    };
    //迷宫标记数组于迷宫数组等大,
    //代表该点是不是已经走过了,如果走过了标记为1,如果可以走,则标记为0
    public int[][] miGongmarkArray =new int[][]{
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,0},
            {0,0,0,0,0}
    };
    //通过px 和 py数组来实现右下左上的移动顺序
    public int px[] = {0, 1, 0, -1};
    public int py[] = {1, 0, -1, 0};
    //找到标志位,0代表没有找到,1代表找到。
    public int flag = 0;
    //构造方法
    public MiGong(){

    };

    //深度搜索的迭代方法

    /**
     *
     * @param x 输入位置的x坐标
     * @param y 输入位置的y坐标
     */
    //迭代终止条件:如果当前位置是目标坐标,则直接,将找到标志flag置1,然后 return。
    //如果经过4次判断,当前位置左上右下都不能走下去,则直接退出本次迭代,回到上次即可。
    //迭代逻辑:在当前位置有效前提以及找到flag为0的基础上,在标记array中,将本位置置1,代表已经走过了。
    // 然后向左上右下进行迭代判断。
    //返回条件:void
    public void searchDFS(int x,int y){
        System.out.println("****************");
        for(int[]a:miGongmarkArray) {
            System.out.println(Arrays.toString(a));
        }
        //如果找到了出口,则flag置1,直接return即可
        if(miGongArray[x][y] == 1){
            flag = 1;
            return ;
        }

        for(int i=0;i<4;i++){
            //分别向左上右下
            int newx = x + px[i];
            int newy = y + py[i];
            //如果位置满足要求,且flag为0,且没有走过,则进行左上右下的DFS
            if(newx>=0&&newx<miGongArray[0].length&&newy>=0&&newy<miGongArray.length&&flag==0&&miGongmarkArray[newx][newy]==0){
                    //将markArray数组标记为1,代表一直走过了
                    miGongmarkArray[newx][newy] = 1;
                    searchDFS(newx,newy);
                    //将markArray数组标记为0,消除上次标记
                    //miGongmarkArray[x][y] = 0;
            }
        }
    }
}

2.2:有序二维数组找值问题

  二维序列的每一行都是从小到大,每一列也是从下到大,请在该二维数组中,找到指定数值。如何用深度优先搜索来解决这个问题尼?

首先,我们应该分析该问题的特点?每一行都是从小到大,每一列也是从小到大,这样的话,如果我们从左下角开始寻找,则会发现,所有大的值在该点的右侧,所有小于该点的值在该点上侧,所以我们也可以设计深度优先算法来找到该值。

代码示例:

/*
使用深度优先搜索算法寻找二维数组中的值的问题
本例子
 */
class ArraySearch {
    public int[][]array = new int[][]{
            {1,5,9 ,13},
            {2,6,10,14},
            {3,7,11,15},
            {4,8,12,16}
    };
    public int[][]markArray = new int[][]{
            {0,0,0,0},
            {0,0,0,0},
            {0,0,0,0},
            {0,0,0,0}
    };
    public int flag = 0;

    //思路:通过深度搜索的方法找到目标值,根据当前位置值和目标值之间的关系来判断
    //下次搜索的位置,直到找到目标值为止。
    //迭代终止条件和处理:当找到目标值时,直接return,或者已经超过边界也没找到,
    //迭代递归逻辑:当前坐标值如果大于目标值,则向上迭代寻找,如果小于着向右迭代寻找。
    //迭代返回:void
    public void searchArray(int x,int y,int k){
        //******迭代结束条件******
        //x和y到达边界,可能始终找不到值,
        if(x<0||y>=array[0].length){
            System.out.println("No Found!!");
            return ;
        }
        //如果找到了目标值,则打印路径,直接return
        if(array[x][y] == k){
            for(int[]a:markArray) {
                System.out.println(Arrays.toString(a));
            }
            flag = 1;
            return ;
        }
        //******迭代逻辑******
        markArray[x][y] = 1;
        if(array[x][y]>k){
            //如果当前值大于目标值,则应向上寻找
            searchArray(x-1,y,k);
        }
        else{
            //如果当前值小于目标值,则应向右寻找{
            searchArray(x, y+1,k);
        }
    }

}

2.3:图的遍历问题

  使用深度优先搜索遍历如下图2.3.1的图。

图2.3.1

  应该如何操作尼?这取决于我们如何表达图这种数据结构,一般我们采用邻接矩阵的方式表达。没有两两相连的我们认为他们之间的距离是Integer.MAX = 65535,而相连的之间就用正常之间的数值来表示即可!!!所以我们有下图所示的数值和矩阵来表达图这种数据结结构。

class SearchInGraph {
    public class Vert{
        public String lable;
        public boolean wasVisted = false;
        public Vert(String lable){
            this.lable = lable;
        }
    }
    //顶点数组
    Vert n0 = new Vert("n0");
    Vert n1 = new Vert("n1");
    Vert n2 = new Vert("n2");
    Vert n3 = new Vert("n3");
    Vert n4 = new Vert("n4");
    Vert n5 = new Vert("n5");
    Vert n6 = new Vert("n6");
    Vert n7 = new Vert("n7");
    Vert n8 = new Vert("n8");
    public Vert[] nVerts = new Vert[]{n0,n1,n2,n3,n4,n5,n6,n7,n8};

    //邻接矩阵
    public int[][] array = new int[][]{
            {0,1,1,0,0,0,0,0,0},//n0
            {1,0,1,1,1,0,0,0,0},//n1
            {1,1,0,0,1,1,0,0,0},//n2
            {0,1,0,0,1,0,1,0,0},//n3
            {0,1,1,1,0,1,1,1,0},//n4
            {0,0,1,0,1,0,0,1,0},//n5
            {0,0,0,1,1,0,0,1,1},//n6
            {0,0,0,0,1,1,1,0,1},//n7
            {0,0,0,0,0,0,1,1,0} //n8
    };

    /**
     *
     * @param v 起始顶点编号
     */
    //深度查询思路:从起始地点开始,按照邻接矩阵中相连顶点前后顺序进行进一步的深度搜索。
    //递归终止条件:当所有顶点都被访问过了,则结束本次递归,
    // 或者当前顶点所有相连顶点都已经标记访问过的了,就可以结束本轮递归了(结束本轮递归,是指本次方法就结束了,不在进行递归下去。)
    //递归逻辑:在当前位置的为标记的相邻顶点,再次进行DFS
    //递归返回值:void
    public void searchDFS(int v){
        //递归终止条件:可以包含在递归逻辑里面
        //将本节点标记为访问过了。
        nVerts[v].wasVisted = true;
        //打印当前节点
        System.out.println(nVerts[v].lable);

        //在当前节点的相邻顶点,找有效顶点
        for(int i=0;i<nVerts.length;i++){
            if(nVerts[i].wasVisted==false&&array[v][i]==1){
                searchDFS(i);
            }
        }
    }
}

2.4:一维数组的全排序问

  该案例是用来,获取当前序列的全排列问题的,比如:数字123的全排列共有6个:123,132,213,231,312,321共计6个如何获取尼?
可以可以采用深度搜索的思想。

  我们以123为例子,首先假设我们第一个数取的是1,那么要标记1位已经选择了,然后在1,2,3里面再选择一个没有标记的,那先选的是2,同时也要把2标记为已经选择,然后再选择一个没有标记的,则只有3了。本次找到了一个序列123,然后再归来的过程将,3,2解标记,同时再归来到第1次再123中选择时,会进行先选择3的时候。这时候就可以产生132序列了!

/**
 * 该案例是用来,获取当前序列的全排列问题的。
 * 比如:数字123的全排列共有6个:123,132,213,231,312,321共计6个
 * 如何获取尼?
 * 可以可以采用深度搜索的思想
 */
class WholeOrderInArray {
    //原始序列
    public int[] array = new int[]{1,2,3,4,5};
    //标记序列
    public int[] markArray = new int[]{0,0,0,0,0};
    //用来判断是不是所有序列都已经标记了的
    int sum = 0;
    //用来存放当前序列的队列,方便我们加入和取出
    Stack<Integer> stack = new Stack<>();
    //深度搜索思路:暂时选择一个数字,标记为已经选择,然后遍历序列选择一个没有被标记的,组成两个数字的序列。
    //不断重读,注意的是,要再回溯的过程,清楚点本轮递归的标记和选择的数据,这是因为我们获得全序列。
    //递归终止条件:当前循环已经结束,则递归结束,本轮递归终止条件是没有只有一个数据,加入后,则将stack内数据输出即可。
    //起始位置
    public void getWholeOrderDFS(){
        //*******递归终止条件*******
        //每次迭代sum都要清零
        sum = 0;
        for(int i:markArray){
            sum = sum + i;
        }
        //sum = length-1意味着,当前只剩下最后一个数字了,直接加入然后return即可
        if(sum == markArray.length){
            for(Integer i:stack){
                System.out.print(i);
            }
            System.out.println();
            return ;
        }
        //*******递归逻辑*******
        for(int i=0;i<array.length;i++){
            //当前数,没有被标记
            if(markArray[i]==0){
                markArray[i] = 1;
                stack.push(array[i]);
                getWholeOrderDFS();
            //再本次递归中结束后,即再回溯的过程,要将本次放进去的数据取出来。为下次选择做准备
                stack.pop();
                markArray[i] = 0;
            }
        }
    }
}

2.5:一维序列的找子序列问题

当前做法不是DFS的做法,但是我相信DFS也可以做,加油加油

2.5.1:重复子序列

2.5.2:顺子子序列

//满足数字中有顺子或者豹子的号码即为靓号
class BeautifulNumber{
    //记录豹子和顺子的长度
    int baoZhi = 0;
    int shunZhi = 0;
    //思路:从头遍历号码,再当前数字处,进行顺子或者豹子的判断,
    public void judgeValue(int[]number){
        int length = number.length;

        int counter;
        //从头遍历得到每一个数字,
        for(int i=0;i<length;i++){

            //***********以该数字为起点,进行豹子判断********
            int index = i;
            counter = 1;
            while(true){
                if(index>=length-1){
                    break;
                }
                if(number[index] !=number[index+1]){
                    break;
                }
                else{
                    counter++;
                    index++;
                }
            }
            //将更长的豹子长度放入baoZhi变量中,
            if(counter > baoZhi){
                baoZhi = counter;
            }



            //***********判断顺子。包括递增和递减**********
            //变量归位
            index = i;
            counter = 1;
            while(true){
                if(index>=length-1){
                    break;
                }
                //如果,满足升序,这对变量操作,继续判断。
                if(number[index+1]- number[index]  ==1){
                    counter++;
                    index++;
                }
                else{
                    break;
                }
            }
            //将更长的豹子长度放入baoZhi变量中,
            if(counter > shunZhi){
                shunZhi= counter;
            }
        }
        System.out.println("豹子数长度为:"+baoZhi);
        System.out.println("顺子数长度为:"+shunZhi);
    }
}

3:总结归纳

  1. DFS深度优先搜索算法,讲究搜索的过程,就是去那里搜索的,即搜索策略的问题;迷宫里,按照既定方向,二维数组里按照判断大小,图中按照相邻顶点是不是被标记进行判断。这恶是关键的!!!
  2. 就是有搜索后,标记的过程,只要本位置被搜索过了就要标记住,防止后序搜索中,再重新走过。
  3. 再全子序列问题中,合适的去标记以及去字符是这个问题解决的关键!!!原因尼?这个不是简单的找到一个就行,而是所有序列都要找到,所以要及时清除标记和取出此次字符,为下次的搜索获取全序列做准备。

猜你喜欢

转载自www.cnblogs.com/dazhu123/p/12565285.html