【LeetCode】无权图的最短路精选7题——单源、多源

news2025/1/12 15:44:45

目录

无权图的单源最短路问题:

1. 迷宫中离入口最近的出口(中等)

2. 最小基因变化(中等)

3. 单词接龙(困难)

4. 为高尔夫比赛砍树(困难)

无权图的多源最短路问题:

1. 01矩阵(中等)

2. 地图中的最高点(中等)

3. 地图分析(中等)


无权图的单源最短路问题:

使用单源BFS可以求解无权图的单源最短路问题,这是由BFS总是按照距离由近到远来遍历图中每个顶点的性质决定的。

1. 迷宫中离入口最近的出口(中等)

class Solution {
public:
    int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {
        int m = maze.size();
        int n = maze[0].size();
        vector<vector<bool>> visited(m, vector<bool>(n));
        int ans = 0;

        // 坐标上下左右的偏移量
        int dr[4] = { -1,1,0,0 };
        int dc[4] = { 0,0,-1,1 };

        queue<pair<int, int>> q;
        // 起始坐标入队并标记
        q.push({entrance[0], entrance[1]});
        visited[entrance[0]][entrance[1]] = true;
        while (!q.empty())
        {
            // 要向外拓展一层,步数++
            ans++;
            int count = q.size(); // 本层坐标点的个数
            for (int i = 0; i < count; i++)
            {
                // 队头出队
                auto [row, col] = q.front();
                q.pop();
                // 判断刚才出队的坐标上下左右是否满足条件,再判断是否到达出口,到达则直接返回,没到达则入队并标记
                for (int j = 0; j < 4; j++)
                {
                    int r = row + dr[j];
                    int c = col + dc[j];
                    if (r >= 0 && r < m && c >= 0 && c < n && maze[r][c] == '.' && !visited[r][c])
                    {
                        if (r == 0 || r == m - 1 || c == 0 || c == n - 1) // 到达出口
                            return ans;
                        q.push({r, c});
                        visited[r][c] = true;
                    }
                }
            }
        }
        return -1;
    }
};

2. 最小基因变化(中等)

将距离为1(变化了一个字符)的字符串连接起来构成一个无权图,求start到end的最短路。

class Solution {
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) {
        unordered_set<string> hash(bank.begin(), bank.end()); // 哈希表记录基因库中的字符串
        unordered_set<string> visited; // 标记字符串是否被访问过
        string change = "ACGT";

        if (startGene == endGene)
            return 0;
        if (!hash.count(endGene))
            return -1;

        int ans = 0;
        queue<string> q;
        // 起始字符串入队并标记
        q.push(startGene);
        visited.insert(startGene);
        while (!q.empty())
        {
            // 要向外拓展一层,步数++
            ans++;
            int count = q.size(); // 本层字符串的个数
            for (int i = 0; i < count; i++)
            {
                // 队头出队
                string cur = q.front();
                q.pop();
                // 判断刚才出队的字符串只改变一个字符后是否满足条件,再判断是否到达end,到达则直接返回,没到达则入队并标记
                for (int i = 0; i < 8; i++)
                {
                    string tmp = cur;
                    for (int j = 0; j < 4; j++)
                    {
                        tmp[i] = change[j];
                        if (hash.count(tmp) && !visited.count(tmp))
                        {
                            if (tmp == endGene) // 到达end
                                return ans;
                            q.push(tmp);
                            visited.insert(tmp);
                        }
                    }
                }
            }
        }
        return -1;
    }
};

3. 单词接龙(困难)

和上一题“最小基因变化”类似,区别是上一题求步数,本题求最短路的顶点数。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> hash(wordList.begin(), wordList.end()); // 哈希表记录字典中的单词
        unordered_set<string> visited; // 标记单词是否被访问过

        if (!hash.count(endWord))
            return 0;

        int ans = 1;
        queue<string> q;
        // 起始单词入队并标记
        q.push(beginWord);
        visited.insert(beginWord);
        while (!q.empty())
        {
            // 要向外拓展一层,顶点数++
            ans++;
            int count = q.size(); // 本层单词的个数
            for (int i = 0; i < count; i++)
            {
                // 队头出队
                string cur = q.front();
                q.pop();
                // 判断刚才出队的单词只改变一个字符后是否满足条件,再判断是否到达end,到达则直接返回,没到达则入队并标记
                for (int i = 0; i < cur.size(); i++)
                {
                    string tmp = cur;
                    for (char ch = 'a'; ch <= 'z'; ch++)
                    {
                        tmp[i] = ch;
                        if (hash.count(tmp) && !visited.count(tmp))
                        {
                            if (tmp == endWord) // 到达end
                                return ans;
                            q.push(tmp);
                            visited.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;
    }
};

4. 为高尔夫比赛砍树(困难)

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7

给树按从小到大排序,求顺序相邻的树的最短路。

class Solution {
public:
    int cutOffTree(vector<vector<int>>& forest) {
        m = forest.size();
        n = forest[0].size();

        // 给树按从小到大排序,确定砍树的顺序
        vector<pair<int, int>> trees;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (forest[i][j] > 1)
                {
                    trees.push_back({i, j});
                }
            }
        }
        sort(trees.begin(), trees.end(), [&](const pair<int, int>& p1, const pair<int, int>& p2)
        {
            return forest[p1.first][p1.second] < forest[p2.first][p2.second];
        });

        // 求顺序相邻的树的最短路
        int beginr = 0;
        int beginc = 0;
        int ans = 0;
        for (auto& [endr, endc] : trees)
        {
            int step = bfs(forest, beginr, beginc, endr, endc);
            if (step == -1)
                return -1;
            ans += step;
            beginr = endr;
            beginc = endc;
        }
        return ans;
    }

private:
    int bfs(vector<vector<int>>& forest, int beginr, int beginc, int endr, int endc)
    {
        if (beginr == endr && beginc == endc)
            return 0;
        
        vector<vector<bool>> visited(m, vector<bool>(n)); // 标记坐标是否被访问过
        int ans = 0;

        queue<pair<int, int>> q;
        // 起始坐标入队并标记
        q.push({beginr, beginc});
        visited[beginr][beginc] = true;
        while (!q.empty())
        {
            // 要向外拓展一层,步数++
            ans++;
            int count = q.size(); // 本层坐标点的个数
            for (int i = 0; i < count; i++)
            {
                // 队头出队
                auto [row, col] = q.front();
                q.pop();
                // 判断刚才出队的坐标上下左右是否满足条件,再判断是否到达end,到达则直接返回,没到达则入队并标记
                for (int j = 0; j < 4; j++)
                {
                    int r = row + dr[j];
                    int c = col + dc[j];
                    if (r >= 0 && r < m && c >= 0 && c < n && forest[r][c] && !visited[r][c])
                    {
                        if (r == endr && c == endc) // 到达end
                            return ans;
                        q.push({r, c});
                        visited[r][c] = true;
                    }
                }
            }
        }
        return -1;
    }

    // 坐标上下左右的偏移量
    int dr[4] = { -1,1,0,0 };
    int dc[4] = { 0,0,-1,1 };
    
    int m;
    int n;
};

无权图的多源最短路问题:

使用多源BFS可以求解无权图的多源最短路问题,将所有的源点当成一个“超级源点”,问题就变成了单源最短路问题。

1. 01矩阵(中等)

以所有的0为源点开始BFS。

距离数组dist最开始全部初始化为-1,表示没有被访问过,后续要修改成距离(步数)。所以,首先,我们不用创建visited数组标记坐标有没有被访问过;其次,不用创建step变量记录步数,并且不用计算本层坐标点的个数count,也就是说不用确定坐标点是在第几层。

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        int m = mat.size();
        int n = mat[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 全部初始化为-1

        // 坐标上下左右的偏移量
        int dr[4] = { -1,1,0,0 };
        int dc[4] = { 0,0,-1,1 };
        
        queue<pair<int, int>> q;
        // 所有的源点入队并修改距离为0
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (mat[i][j] == 0)
                {
                    q.push({i, j});
                    dist[i][j] = 0;
                }
            }
        }
        while (!q.empty())
        {
            // 队头出队
            auto [row, col] = q.front();
            q.pop();
            // 判断刚才出队的坐标上下左右是否满足条件,满足条件则入队并修改距离
            for (int j = 0; j < 4; j++)
            {
                int r = row + dr[j];
                int c = col + dc[j];
                if (r >= 0 && r < m && c >= 0 && c < n && dist[r][c] == -1)
                {
                    q.push({r, c});
                    dist[r][c] = dist[row][col] + 1;
                }
            }
        }
        return dist;
    }
};

2. 地图中的最高点(中等)

水域格子的值是确定的,为0,以所有的0为源点开始BFS,和上一题“01矩阵”一模一样。

class Solution {
public:
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {
        int m = isWater.size();
        int n = isWater[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 全部初始化为-1

        // 坐标上下左右的偏移量
        int dr[4] = { -1,1,0,0 };
        int dc[4] = { 0,0,-1,1 };
        
        queue<pair<int, int>> q;
        // 所有的源点入队并修改距离为0
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (isWater[i][j] == 1)
                {
                    q.push({i, j});
                    dist[i][j] = 0;
                }
            }
        }
        while (!q.empty())
        {
            // 队头出队
            auto [row, col] = q.front();
            q.pop();
            // 判断刚才出队的坐标上下左右是否满足条件,满足条件则入队并修改距离
            for (int j = 0; j < 4; j++)
            {
                int r = row + dr[j];
                int c = col + dc[j];
                if (r >= 0 && r < m && c >= 0 && c < n && dist[r][c] == -1)
                {
                    q.push({r, c});
                    dist[r][c] = dist[row][col] + 1;
                }
            }
        }
        return dist;
    }
};

3. 地图分析(中等)

以所有的陆地单元格为源点开始BFS,陆地单元格对应在距离数组dist中的值为0,和“01矩阵”、“地图中的最高点”都是一样的题。

class Solution {
public:
    int maxDistance(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 全部初始化为-1
        int ans = -1;

        // 坐标上下左右的偏移量
        int dr[4] = { -1,1,0,0 };
        int dc[4] = { 0,0,-1,1 };
        
        queue<pair<int, int>> q;
        // 所有的源点入队并修改距离为0
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (grid[i][j] == 1)
                {
                    q.push({i, j});
                    dist[i][j] = 0;
                }
            }
        }
        while (!q.empty())
        {
            // 队头出队
            auto [row, col] = q.front();
            q.pop();
            // 判断刚才出队的坐标上下左右是否满足条件,满足条件则入队并修改距离
            for (int j = 0; j < 4; j++)
            {
                int r = row + dr[j];
                int c = col + dc[j];
                if (r >= 0 && r < m && c >= 0 && c < n && dist[r][c] == -1)
                {
                    q.push({r, c});
                    dist[r][c] = dist[row][col] + 1;
                    ans = max(ans, dist[r][c]);
                }
            }
        }
        return ans;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1459523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML元素和属性快速参考指南

​ 以下是几个与HTML元素和属性相关的参考资料网站链接&#xff1a; HTML Reference - 提供所有HTML元素和属性的免费指南。W3Schools HTML Reference - W3Schools 提供一个广泛的HTML标签和属性参考。freeCodeCamp HTML Cheat Sheet - freeCodeCamp 提供了一个HTML元素列表参…

docker (十一)-进阶篇-docker-compos最佳实践部署zabbix

一 部署docker环境 关闭防火墙、selinux、开启docker&#xff0c;并设置开机自启动 注意点&#xff1a;docker部署的时候&#xff0c;bip要指定&#xff0c;不然会导致虚拟机ip和容器ip冲突&#xff0c;ssh连不上虚拟机 部署请参考 docker &#xff08;二&#xff09;-yum…

【机器学习】数据清洗——基于Numpy库的方法删除重复点

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

Github 2024-02-16 开源项目日报 Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-16统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目3Rust项目3Jupyter Notebook项目1JavaScript项目1 Black&#xff1a;不妥协的Pytho…

并发编程-ReentrantLock 与 ReentrantReadWriteLock(可重入锁,独享锁,公平与非公平锁,读写锁)

AQS实现原理 前期准备 AQS&#xff08;全称AbstractQueuedSynchronizer&#xff09;即队列同步器。它是构建锁或者其他同步组件的基础框架&#xff08;如ReentrantLock、ReentrantReadWriteLock、Semaphore等&#xff09;。 整体概览类图 AbstractQueuedSynchronizer类图 …

汽车控制器软件正向开发

需求常见问题: 1.系统需求没有分层,没有结构化,依赖关系不明确 2.需求中没有验证准则 3.对客户需求的追溯缺失,不完整,颗粒度不够 4.系统需求没有相应的系统架构,需求没有分解到硬件和软件 5.需求变更管控不严格,变更频繁,变更纪录描述不准确,有遗漏,客户需求多…

【MySQL】如何处理DB读写分离数据不一致问题?

文章内容 1、前言读写库数据不一致问题我们如何解决&#xff1f;方案一&#xff1a;利用数据库自身特性方案二&#xff1a;不解决方案三&#xff1a;客户端保存法方案四&#xff1a;缓存标记法方案五&#xff1a;本地缓存标记 那DB读写分离情况下&#xff0c;如何解决缓存和数据…

STM32F1X RS485使用DMA发送丢失数据的处理方法。

串口通过DMA发送一帧数据时总是缺少2个字节&#xff0c;且最后一个字节数据为0xff的原因及解决方法 本次记录为采用485串口发送数据&#xff0c;发送模式是循环检测串口数据寄存器为空&#xff08;TXE&#xff09;和发送完成标志位&#xff08;TC&#xff09;。DMA发送串口方式…

基于java,springboot和vue房屋租赁租房销售平台设计

摘要 在现代城市生活中&#xff0c;房屋租赁市场一直是一个活跃且复杂的领域。随着互联网技术的不断发展&#xff0c;基于Spring Boot和Vue的房屋租赁系统应运而生&#xff0c;旨在提供一个高效、方便、可靠的在线服务平台。该系统利用了前后端分离架构的优势&#xff0c;后端…

【嵌入式学习】QT-Day1-Qt基础

笔记 https://lingjun.life/wiki/EmbeddedNote/20QT 毛玻璃登录界面实现&#xff1a;

模式匹配这么好,Java语法里有吗?

这篇文章我们借助新版Java来理解模式匹配&#xff0c;Rust版的模式匹配稍后就端上来&#xff0c;各位先尝尝Java这杯老咖啡还香不香&#x1f604;。 什么是模式匹配&#xff1f; 下图直观的表达了模式匹配的概念。 所谓模式类似上图中木盒的各种形状的洞洞&#xff0c;我们…

UG NX二次开发(C#)-PMI-获取PMI尺寸数据

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX的三维模型中添加PMI尺寸信息3、采用二次开发获取尺寸数据4、测试结果1、前言 PMI(Product and Manufacturing Information)是产品和制造信息的简称,主要用于将产品部件设计的…

工具分享:在线键盘测试工具

在数字化时代&#xff0c;键盘作为我们与计算机交互的重要媒介之一&#xff0c;其性能和稳定性直接影响到我们的工作效率和使用体验。为了确保键盘的每个按键都能正常工作&#xff0c;并帮助用户检测潜在的延迟、连点等问题&#xff0c;一款优质的在线键盘测试工具显得尤为重要…

智能运维乱象有哪些?智能运维业务包括哪些

在实施智能运维过程中可能遇到的乱象及其原因&#xff0c;系统地阐述智能运维业务所涵盖的各个方面&#xff0c;包括但不限于预防性维护、故障检测与诊断、自动化修复以及持续的性能优化等关键组成部分。 实施智能运维过程中可能遇到的乱象及原因包括&#xff1a; 数据不一致或…

Qt|大小端数据转换(补充)

Qt|大小端数据转换-CSDN博客 之前这篇文章大小端数据转换如果是小数就会有问题。 第一个方法&#xff1a; template <typename T> static QByteArray toData(const T &value, bool isLittle) {QByteArray data;for (int i 0; i < sizeof(T); i) {int bitOffset…

小米14 ULTRA:重新定义手机摄影的新篇章

引言 随着科技的飞速发展&#xff0c;智能手机已经不仅仅是一个通讯工具&#xff0c;它更是我们生活中的一位全能伙伴。作为科技领域的佼佼者&#xff0c;小米公司再次引领潮流&#xff0c;推出了全新旗舰手机——小米14 ULTRA。这款手机不仅在性能上进行了全面升级&am…

电脑文件msvcr110.dll缺失的多种解决方法,msvcr110.dll文件修复手段

遭遇"程序无法启动&#xff0c;因为电脑中缺失msvcr110.dll"这样的错误提示&#xff0c;是Windows操作系统用户可能会遇到的一种情况。尽管这种现象在一些用户中较为常见&#xff0c;但解决这一问题并非复杂的过程。本文将深入剖析此问题&#xff0c;并分享一些实用的…

2.16日学习打卡----初学Dubbo(一)

2.16日学习打卡 目录: 2.16日学习打卡一. 什么是分布式&#xff1f;二. 什么是RPC?三. Dubbo概念_简介四. Dubbo核心组件五.Dubbo配置开发环境六. Dubbo配置开发环境_管理控制台 一. 什么是分布式&#xff1f; 可以看我的这篇文章–2.14日学习打卡----初学Zookeeper(一) 二.…

【设计模式】23种设计模式笔记

设计模式分类 模板方法模式 核心就是设计一个部分抽象类。 这个类具有少量具体的方法&#xff0c;和大量抽象的方法&#xff0c;具体的方法是为外界提供服务的点&#xff0c;具体方法中定义了抽象方法的执行序列 装饰器模式 现在有一个对象A&#xff0c;希望A的a方法被修饰 …

Android挖取原图中心区域RectF(并框线标记)放大到ImageView宽高,Kotlin

Android挖取原图中心区域RectF(并框线标记)放大到ImageView宽高&#xff0c;Kotlin 红色线框区域即为选中的原图中心区域&#xff0c;放大后放到等宽高的ImageView里面。 import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactor…