【优选算法】专题十六——BFS解决最短路径问题

文章目录

关于最短路径问题

最短路径问题是图论中的重要一部分,本篇文章先介绍边权相等(本文中具体是边权均为1)的最短路径问题

  • 该问题的解法总结一句话就是:"从起点开始来一遍BFS就行了"(可以复习完本篇内容之后再来理解这句话)
  • 而关于如何得到最短的路径长度,记录整个BFS的过程所扩展的层数就是最短路径的大小

看图回忆解题思路(基本就相当于是所有可能的路径同时走,最后肯定是最短的那条路径先到终点):

hash的作用就是防止将重复的中间点入队列(说明早就有路径率先到达了该节点,而出现重复的节点就说明有条路径后来才到达该节点,那这条路径就可以直接排除了)

关于将问题转化为"最短路径问题":

  • 转化为最短路问题:路径从A->...->B,也可以是事物状态从A->...->B,每次变化都可以看做是"在走过一段路",每次变化后的结果就是一个节点,一次变化可能产生多个结果,那就是等价于C节点->D节点有多条路可以选。而"每次变化的成本相等的最短路问题",也就是"边权相等的最短路径问题"

一、迷宫中离入口最近的出口

Leetcode链接

给你一个 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;
    }
}

总结

  • 复习解题思路和代码实现及解析

二、最小基因变化

Leetcode链接

基因序列可以表示为一条由 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算法结合的、细节是怎么处理的(尤其注意基因变化那一步)
相关推荐
liuyao_xianhui1 小时前
优选算法_两数之和_位运算_C++
java·开发语言·数据结构·c++·算法·链表·动态规划
博风1 小时前
算法:双指针解:盛最多水的容器
算法·leetcode
知识分享小能手1 小时前
edis入门学习教程,从入门到精通,Redis编程开发知识点详解(4)
数据库·redis·学习
夏乌_Wx2 小时前
图 | 3道LeetCode常见笔试、面试题汇总
算法
ZPC82102 小时前
PPO训练小车
人工智能·算法·机器人
bksczm2 小时前
二分查找的细则(binary search)
算法
A923A2 小时前
【洛谷刷题 | 第三天】
算法·二分·洛谷·pair