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
- 数据城堡参赛代码实战篇(六)---使用sklearn进行数据标准化及参数寻优
- 震惊!Vector两行代码求逆序对,六行代码过普通平衡树
- 数据城堡参赛代码实战篇(五)---使用sklearn解决分类问题
- 洛谷P1894 [USACO4.2]完美的牛栏The Perfect Stall
- [编程经验]Python生成器、迭代器与yield语句小结
- TensorFlow从0到1 - 12 - TensorFlow构建3层NN玩转MNIST
- 数据城堡参赛代码实战篇(四)---使用pandas合并数据表
- HDU 2586 How far away ?
- HDU 3078 Network
- 数据城堡参赛代码实战篇(三)---我们来探究一个深奥的问题!
- 数据城堡参赛代码实战篇(二)---使用pandas进行数据去重
- 洛谷P3375 【模板】KMP字符串匹配
- Day5下午解题报告1
- [编程经验] Python中处理时间的方法小结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 关于rxjs里operators filter和map的详细讨论
- 用代码查看SAP Spartacus购物车内的行项目
- rxjs的map和switchMap在SAP Spartacus中的应用
- 用代码查看SAP Spartacus购物车内的行项目
- rxjs fromEvent的用法
- Python2和Python3的区别简单总结
- Django操作数据库
- Hive元数据服务MetaStore
- Linux---Shell脚本字符显示特殊颜色效果
- 快速学习-RocketMQ运维管理
- Django常用语句
- 快速学习-RocketMQ样例
- 快速学习-RocketMQ-“Request-Reply”特性
- 牛逼!Docker遇到Intellij IDEA,再次解放了生产力~
- Go IP 段范围校验