LeetCode1293网格中的最短路径(DFS和BFS)分析

时间:2020-07-02
本文章向大家介绍LeetCode1293网格中的最短路径(DFS和BFS)分析,主要包括LeetCode1293网格中的最短路径(DFS和BFS)分析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

 先说明一下:本体正确解法使用BFS(广度优先),最下方的是关于BFS的思路

BFS与DFS最大的区别在于:

      BFS首先搞同一层,然后从同一层一个个出发,找下一层

      DFS首先搞一个,一直搞到底,然后再搞下一个,也就是回溯

接下来,我首先分析一下,我的整个思路的分析~~

==超时的个人思路:=

对于我个人来讲,看到题的第一反应是使用DFS(深度优先),很简单嘛,思路就是:

从0,0开始,可以往上下左右方向走,我只需要确认:1.我下面走的一步没有超出边界;2.下面的一步没有被走过即可

当走到了rowIndex-1,colIndex-1时,说明找到一条走法,将本次走过的长度加入到全局的ArrayList中,最后找出最小的那个就好啦。

当然本题中多出来一个k,代表我们可以破解边界的次数,这个就是当前如果是边界,且能破的话,ok那就破,继续和上方一样,否则就结束啦。

==end==

思路是不是非常清晰,ok,我们上代码

如果没有边界的话,最短的长度一定是row+col-2(因为0,0不算),(rowIndex-1,colIndex-1是我们需要到达的,因此一定不是1)那么当我们可以破接障碍物的次数>=row+col-3的时候,那么这个全局最短路径一定是可行的啦!

代码中 visited[rowIndex][colIndex] = true;后最下方又设置为false的原因,就是回溯的思想,本次操作完,还原,进行下一个节点的深度查找!

  //有k次可以清除障碍物的机会
    //每次走的时候我都选择一下,清除和不清楚,这样既有只要有障碍物就清除的,也有走到最后一个障碍物都没q清除的
    public int shortestPath(int[][] grid, int k) {
        boolean[][] visited = new boolean[grid.length][grid[0].length];
        if(k>=grid.length+grid[0].length-3)return grid.length+grid[0].length-2;
        findPath(grid, 0, 0, k, visited, 0);
        if (countList.size() == 0) return -1;
        int minV = countList.get(0);
        for (int i = 1; i < countList.size(); i++) {
            minV = minV < countList.get(i) ? minV : countList.get(i);
        }
        return minV;
    }

    //策略:下,右,左,上(不能搞策略,应该全部走一遍看看)
    //k代表还有几次清除的机会
    ArrayList<Integer> countList = new ArrayList<>();

    private void findPath(int[][] grid, int rowIndex, int colIndex, int k, boolean[][] visited, int count) {
        if (rowIndex == grid.length - 1 && colIndex == grid[0].length - 1) {
            countList.add(count);
        }
        if (rowIndex >= 0 && rowIndex < grid.length && colIndex >= 0 && colIndex < grid[0].length && !visited[rowIndex][colIndex]) {
            //破障碍
            //当前存在障碍,只有有破障碍的机会的时候才能继续往下走
            if (grid[rowIndex][colIndex] == 1) {
                if (k > 0) {
                    visited[rowIndex][colIndex] = true;
                    findPath(grid, rowIndex - 1, colIndex, k - 1, visited, count + 1);//
                    findPath(grid, rowIndex + 1, colIndex, k - 1, visited, count + 1);//
                    findPath(grid, rowIndex, colIndex - 1, k - 1, visited, count + 1);//
                    findPath(grid, rowIndex, colIndex + 1, k - 1, visited, count + 1);//
                    visited[rowIndex][colIndex] = false;
                }
            } else {//如果当前不存在障碍,那么直接找
                visited[rowIndex][colIndex] = true;
                findPath(grid, rowIndex - 1, colIndex, k, visited, count + 1);//
                findPath(grid, rowIndex + 1, colIndex, k, visited, count + 1);//
                findPath(grid, rowIndex, colIndex - 1, k, visited, count + 1);//
                findPath(grid, rowIndex, colIndex + 1, k, visited, count + 1);//
                visited[rowIndex][colIndex] = false;
            }
        }
    }

感觉写的没啥问题,但是很遗憾,超时啦,难受~

==BFS解法思路(可行)==

 首先看到了BFS的模板,感觉还不错哦,贴出来,分析~

我们可以看到,首先初始化一个队列,当然广度优先需要一层的元素,因此我们最开始需要把第一个节点放进去

然后,每次进来的时候,遍历当前层所有节点,当然每遍历一个,我们就删除它,最后将本层全部遍历完,队列中留的就是下一层的全部

就这样逐层找,看看能否找到最后一层我们需要的节点,如果能则返回,如果都找了,就是找不到最后一层的某个节点,队列也空了,那么说明确实找不到了,ok,retrun false或者找不到就好啦。

// 节点访问标识,访问过的节点无需访问(剪枝)
int[][] visited = new int[m][n];
// 队列初始化
Queue<Node> queue = new LinkedList();

// 【第1步】将起点加入队列, 非空进入循环
queue.add(第一个数据)
while(!queue.isEmpty()) {
    // 【第2步】 获取当前队列长度即同一层级(辈分)节点个数,并遍历
    int size = queue.size(); // 一定要先获取,queue后面要加入下一层级节点
    for (int i = 0; i < size; i++) {
        // 【第3步】 对同一层级节点逐个寻找下一层有效**路径节点**,找到目标直接返回结果终止搜索。
        Node node = queue.poll();
        // 下一层节点 比如网格上下左右移动
        Node nextNode = node.x + xj;
        //  1. 不符合要求的下一层节点直接过滤(比如越界、已经被visited[][]标记访问了)
        //  2. 找到目标节点 直接返回结果
        //  3. 符合要求的下一层节点放入队列
        queue.offer(nextNode)
    }
}
// 【第4步】 BFS搜索完成没找到结果,返回-1
return -1;

===BFS算法代码实现===

创建一个类,用于记录当前位置和跨越的障碍数

 class Point {
        int x;
        int y;
        int oneCount;

        public Point(int x, int y, int oneCount) {
            this.x = x;
            this.y = y;
            this.oneCount = oneCount;
        }
    }
  public int shortestPathOK(int[][] grid, int k) {
        int row=grid.length,col=grid[0].length;
        if(k>=row+col-3)return row+col-2;
        int[][] visited=new int[row][col];//没走过则为-1 ,否则为当前可破解障碍的剩余次数
        for (int i = 0; i < visited.length; i++) {
            for (int j = 0; j < visited[0].length; j++) {
                visited[i][j]=-1;
            }
        }
        visited[0][0]=k;//0,0点剩余k次破解障碍
        Queue<Point> queue=new LinkedList<>();
        Point point=new Point(0,0,0);
        queue.add(point);
        Point temp;//用于内部接收当前的队列元素
        // 定义四个方向移动坐标
        int[] dx = {1, -1, 0, 0};
        int[] dy = {0, 0, 1, -1};
        int moveNum=0;
        while (!queue.isEmpty()){
            moveNum++;//因为是广度优先,移动一次,加一次就好
            int size=queue.size();//当前层的个数
            for(int i=0;i<size;i++){
                temp=queue.poll();
                //需要从当前节点往上下左右移动
                for(int j=0;j<4;j++){
                    int xNew=dx[j]+temp.x;
                    int yNew=dy[j]+temp.y;
                    //不能越界
                    if(xNew<0||xNew>=row||yNew<0||yNew>=col){
                        continue;
                    }
                    int oneCountNew=temp.oneCount+grid[xNew][yNew];

                    if(xNew==row-1&&yNew==col-1){
                        return moveNum;
                    }
                    //当前是障碍物,但是已经没有穿越障碍物的机会了
                    if(grid[xNew][yNew]==1&&oneCountNew>k){
                        continue;
                    }
                    //如果当前被访问过,因为是BFS,说明访问次数是不大于当前的访问次数的,如果剩余可破解的障碍物数也不比他小,那当前走法一定不是一个好的走法
                    if(visited[xNew][yNew]!=-1&&visited[xNew][yNew]>=k-oneCountNew){
                        continue;
                    }
                    else {
                        //这里为什么可以改呢?
                        //因为这里是广度优先,因为从当前位置走只能上下左右,不能走到同一个位置上
                        //因此这里走过了,要么这个点在另一条路径上已经不在需要,要么他们走的步长是一样的
                        //如果不需要则我修改对原路径也没有任何影响
                        //如果步长一样,本次剩余跨障碍的次数多,当时这个好啦
                        visited[xNew][yNew]=k-oneCountNew;
                    }
                    queue.offer(new Point(xNew,yNew,oneCountNew));
                }
            }
        }
        //没找到
        return -1;
    }

原文地址:https://www.cnblogs.com/ningxinjie/p/13227030.html