算法_BFS解决最短路问题---持续更新

前言

本文将会向你介绍有关

迷宫中离入口最近的出口

https://leetcode.cn/problems/nearest-exit-from-entrance-in-maze/

题目要求

在这里插入图片描述

题目解析

很典型的一道题,迷宫问题,可以上下左右移动一个格子,要求返回到边界出口最短路径的步数,可以想到用宽度优先遍历,扩散式地向外一层层搜索,每搜索一层就step步数++。直到抵达出口
需要注意的是,与前文BFS解决floodfill算法不同的是该类型题需要求最短路径,也就是我们需要保证该层已经统计完了,然后再统计下一层,
在代码中的体现就是,相对于floodfill多了一层for循环

for(int i = 0; i < sz; i++)
这层for循环是保证我们在广度优先搜索(BFS)中,会逐层处理节点。sz 代表当前队列中节点的数量,也就是当前层的节点数。通过这个循环,我们可以确保在处理完当前层的所有节点后,再进入下一层,如果没有这个循环,可能会在处理队列中的节点时,直接将新加入的节点也处理掉。而我们的逻辑是处理完一层的结点,步数+1,这一层循环是必要的,以确保 BFS 的正确性和步数的准确计算。

代码如下

class Solution {
public:
    int nearestExit(vector<vector<char>>& maze, vector<int>& e) 
    {
        int dx[4] = {0, 0, 1, -1};
        int dy[4] = {1, -1, 0, 0};
        int m = maze.size(), n = maze[0].size();
        int step = 0;   //最短路径的步数
        queue<pair<int, int>> q;
        bool vis[m][n];
        memset(vis, 0, sizeof(vis));
        q.push({e[0], e[1]});
        vis[e[0]][e[1]] = true;
        while(q.size())
        {                
            step++;
            int sz = q.size();
            for(int i = 0; i < sz; i++)
            {
                auto [a, b] = q.front();
                q.pop();
                for(int j = 0; j < 4; j++)
                {
                    int x = a + dx[j], y = b + dy[j];
                    if(x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.' && !vis[x][y])
                    {
                        q.push({x, y});
                        if(x == 0 || x == m - 1 || y == 0 || y == n - 1) return step;
                        vis[x][y] = true;
                    }

                }
            }
        }
        return -1;
    }
};

最小基因变化

https://leetcode.cn/problems/minimum-genetic-mutation/

题目要求

在这里插入图片描述

题目解析

该题的本质也是最短路问题,startGene->endGene,endGene是出口
在这里插入图片描述

一次基因变化就意味着基因序列中的一个字符发生了变化,变化:A、C、G、T,但是变化后的基因序列必须要存在于基因库当中,才算得上一次有效的基因序列
那么我们可以暴力地遍历当前基因序列,并将每个字符都从四种变化中变化一次,但是可能会遇到重复的基因序列,可以添加一个
unordered_set标记已经扫描过的基因序列,如果经过变化的基因序列存在于基因库中且未被扫描,就将其添加到队列当中作为一次合法变化路径

if(bankHash.count(tmpGene) && vis.find(tmpGene) == vis.end())   //在基因库中且未访问 
{
	vis.insert(tmpGene); //标记已扫描
	q.push(tmpGene);
}

代码如下

class Solution {
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) 
    {
        unordered_set<string> vis; //标记已经扫描过的状态
        unordered_set<string> bankHash(bank.begin(), bank.end()); //查找该状态是否与基因库中符合
        if(!bankHash.count(endGene)) return -1;
        int steps = 0;
        char arr[4] = {'A', 'C', 'G', 'T'};
        queue<string> q;
        q.push(startGene);
        while(q.size())
        {
            steps++;
            int sz = q.size();
            for(int i = 0; i < sz; i++)
            {
                string changeGene = q.front();
                q.pop();
                for(int j = 0; j < 8; j++)
                {
                    for(int k = 0; k < 4; k++)
                    {
                        string tmpGene = changeGene;
                        tmpGene[j] = arr[k];                            
                        if(tmpGene == endGene) return steps;

                        if(bankHash.count(tmpGene) && vis.find(tmpGene) == vis.end())   //在基因库中且未访问 
                        {
                            vis.insert(tmpGene); //标记已扫描
                            q.push(tmpGene);
                        }
                    }
                }
            }
        }
        return -1;
    }
};

单词接龙

https://leetcode.cn/problems/om3reC/

题目要求

在这里插入图片描述

题目解析

该题的做法与上一题最小基因变化类似,都是最短路径问题,需要注意的是该题的返回值是一个最短转换序列的个数,包含beginWord与endWord在内

代码如下

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) 
    {
        unordered_set<string> wordHash(wordList.begin(), wordList.end()); //用于检测是否是字典中的单词
        unordered_set<string> vis;  //已经扫描
        if(!wordHash.count(endWord)) return 0;
        int steps = 0;
        char c = 'a';
        queue<string> q;
        q.push(beginWord);
        while(q.size())
        {
            steps++;
            int sz = q.size();
            //向外进行一次扩展
            for(int i = 0; i < sz; i++)
            {
                string changeWord = q.front();
                q.pop();
                for(int j = 0; j < changeWord.size(); j++)
                {
                    string tmpWord = changeWord;
                    for(int k = 0; k < 26; k++)
                    {
                        tmpWord[j] = 'a' + k;
                        if(tmpWord == endWord) return steps + 1;
                        if(wordHash.count(tmpWord) && !vis.count(tmpWord))  //在字典中存在且未被扫描
                        {
                            vis.insert(tmpWord);
                            q.push(tmpWord);
                        }   
                    }
                }
            }
        }
        return 0;
    }
};

为高尔夫比赛砍树

https://leetcode.cn/problems/cut-off-trees-for-golf-event/

题目要求

在这里插入图片描述

题目解析

题目要求按照树的高度从低到高砍掉所有的树,从(0,0)点开始,返回砍完所有树的最小步数,如果从(0,0)点开始bfs的话,那么要求从低到高,会有些困难 因此可以多来几次bfs,只需要用一个二维数组保存矩阵中的所有树的下标,并且按照高度从低到高排序,下标作为终点,传入到bfs中即可
for(auto& [a, b] : v)
{
	int steps = bfs(f, bx, by, a, b); //返回最短路径
	if(steps == -1) return -1;
	bx = a, by = b; //当前终点作为下一次出发的起点
	ret += steps;
}

代码如下

class Solution {
public:
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    bool vis[51][51];

    int bfs(vector<vector<int>>& f, int bx, int by, int ex, int ey)
    {
        queue<pair<int, int>> q;    //每次的搜索都是独立的,需要重置队列
        if(bx == ex && by == ey) return 0;
        memset(vis, 0, sizeof(vis));    //重新标记
        q.push({bx, by});  
        vis[bx][by] = true;
        int steps = 0;
        while(q.size())
        {
            steps++;
            int sz = q.size();
            for(int i = 0; i < sz; i++)
            {
                auto [a, b] = q.front();
                q.pop();
                for(int j = 0; j < 4; j++)
                {
                    int x = a + dx[j];
                    int y = b + dy[j];
                    if(x == ex && y == ey)
                    {
                        return steps;
                    }
                    if(x >= 0 && x < f.size() && y >= 0 && y < f[0].size() && !vis[x][y] && f[x][y])
                    {
                        q.push({x, y});
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }

    int cutOffTree(vector<vector<int>>& f) 
    {
        vector<pair<int, int>> v;
        int k = 0;
        int ret = 0; //总最短路径
        for(int i = 0; i < f.size(); i++)
        {
            for(int j = 0; j < f[0].size(); j++)
            {
                if(f[i][j] > 1)
                {
                    v.push_back({i, j});
                }
            }
        }
        sort(v.begin(), v.end(), [&](const pair<int, int>& p1, const pair<int, int>& p2)
        {
            return f[p1.first][p1.second] < f[p2.first][p2.second];
        });
        int bx = 0, by = 0;
        for(auto& [a, b] : v)
        {
            int steps = bfs(f, bx, by, a, b); //返回最短路径
            if(steps == -1) return -1;
            bx = a, by = b; //当前终点作为下一次出发的起点
            ret += steps;
        }
        return ret;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fan_558

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值