文章目录
关于最短路径问题
最短路径问题是图论中的重要一部分,本篇文章先介绍边权相等(本文中具体是边权均为1)的最短路径问题
- 该问题的解法总结一句话就是:
"从起点开始来一遍BFS就行了"(可以复习完本篇内容之后再来理解这句话) - 而关于如何得到最短的路径长度,记录整个BFS的过程所扩展的层数就是最短路径的大小
看图回忆解题思路(基本就相当于是所有可能的路径同时走,最后肯定是最短的那条路径先到终点):

hash的作用就是防止将重复的中间点入队列(说明早就有路径率先到达了该节点,而出现重复的节点就说明有条路径后来才到达该节点,那这条路径就可以直接排除了)
关于将问题转化为"最短路径问题":
- 转化为最短路问题:路径从A->...->B,也可以是事物状态从A->...->B,每次变化都可以看做是"在走过一段路",每次变化后的结果就是一个节点,一次变化可能产生多个结果,那就是等价于C节点->D节点有多条路可以选。而"每次变化的成本相等的最短路问题",也就是"边权相等的最短路径问题"
一、迷宫中离入口最近的出口
给你一个 m x n 的迷宫矩阵 maze (下标从 0 开始),矩阵中有空格子(用 '.' 表示)和墙(用 '+' 表示)。同时给你迷宫的入口 entrance ,用 entrance = [entrancerow, entrancecol] 表示你一开始所在格子的行和列。
每一步操作,你可以往 上,下,左 或者 右 移动一个格子。你不能进入墙所在的格子,你也不能离开迷宫。你的目标是找到离 entrance 最近 的出口。出口 的含义是 maze 边界 上的 空格子。entrance 格子 不算 出口。
请你返回从 entrance 到最近出口的最短路径的 步数 ,如果不存在这样的路径,请你返回 -1 。

解题思路
- 像上面介绍的那样,先把起点入队列,接着就开始进行bfs的过程了(且最短路径问题要分层进行poll()),每次将下一步可能走到的位置(坐标)入队列,直到判断为走到了边缘(也就是出口),记录下的step就是最少的步数。
代码实现及解析
java
class Solution {
//偏移量数组
int[] dx=new int[]{0,0,1,-1};
int[] dy=new int[]{1,-1,0,0};
//记录矩阵(迷宫)每个位置的访问状态的矩阵(防止选择到已经走过的格子)
boolean[][] visited=new boolean[100][100];//一次给够空间,也可以在下面方法中new一个合适的空间
int m,n;
public int nearestExit(char[][] maze, int[] entrance) {
m=maze.length;n=maze[0].length;
int step=0;//记录走过的步数
Queue<int[]> que=new LinkedList<>();
que.offer(new int[]{entrance[0],entrance[1]});
visited[entrance[0]][entrance[1]]=true;//标记已走过这个格子
while(!que.isEmpty()){
int size=que.size();
step++;//接下来队列要再扩展一层了(也就是所有的路径都要往前走一步了),所以记录下这一步(for循环中可能会return,所要提前记录step)
for(int i=0;i<size;i++){
int[] tmp=que.poll();
int a=tmp[0],b=tmp[1];
for(int k=0;k<4;k++){//在上下左右四个方向寻找下一步可能的行走方向
int x=a+dx[k],y=b+dy[k];
if(x>=0&&x<m&&y>=0&&y<n&&maze[x][y]=='.'&&!visited[x][y]){//不能越界&&不能撞墙&&不能被访问过
if(x==0||x==m-1||y==0||y==n-1){//该坐标在矩阵边缘,也就是说到出口了
return step;
}
que.offer(new int[]{x,y});
visited[x][y]=true;
}
}
}
}
return -1;
}
}
总结
复习解题思路和代码实现及解析
二、最小基因变化
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'、'C'、'G' 和 'T' 之一。
假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
例如,"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。
另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank 中)
给你两个基因序列 start 和 end ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。
注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。
示例 1:
输入:start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
输出:1
解题思路
- 将每一次基因的变化视为一次点的移动,基因可以有多种变化 -> 对应一个地点的下一步有多个选择。那么本题就是startGene -> endGene 的最短路径问题。
- 本题是上面介绍的将实际问题转化(抽象为)为"最短路径问题"的一个非常好的代表
- 像上一题一样使用BFS解题,注意不同的细节处理
代码实现及解析
java
class Solution {
public int minMutation(String startGene, String endGene, String[] bank) {
Set<String> bankSet=new HashSet<>();//bank放入哈希表,方便查找
for(String str:bank){
bankSet.add(str);
}
Set<String> recordSet=new HashSet<>();//后续将基因变化后的newGene放入哈希表以供检查,防止基因变化为重复的基因
//这是BFS解决最短路径题目非常重要的一点(不能折返回已经走过的地点)
Queue<String> que=new LinkedList<>();
que.offer(startGene);
recordSet.add(startGene);
char[] change=new char[]{'A','C','G','T'};//便于等会使用基因片段
//BFS核心过程:
int ret=0;//记录变化次数(也就是最短路径)
while(!que.isEmpty()){
ret++;
int size=que.size();
while(size--!=0){//分层进行BFS,每层只poll()出size个元素
String str=que.poll();
//基因变化:
for(int i=0;i<str.length();i++){//每次针对一个字符变化
char[] tmp=str.toCharArray();//更新完str才算是每次只针对一个字符
for(int j=0;j<4;j++){//每个字符又有四种可能的变化
tmp[i]=change[j];
String nextGene=new String(tmp);
if(bankSet.contains(nextGene)&&!recordSet.contains(nextGene)){//对这个新的基因序列进行判断
if(nextGene.equals(endGene)){
return ret;
}
que.offer(nextGene);
recordSet.add(nextGene);
}
}
}
}
}
return -1;
}
}
总结
复习解题思路本题与上一题同为最短路径问题,在代码实现中看看这种实际问题转化为最短路径问题之后是怎么与BFS算法结合的、细节是怎么处理的(尤其注意基因变化那一步)