【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树

这篇具有很好参考价值的文章主要介绍了【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

✅作者简介:热爱后端语言的大学生,CSDN内容合伙人
✨精品专栏:C++面向对象
🔥系列专栏:算法百炼成神


🔥前言

本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面试真题,面试、找工作完全可以来这里找机会。此外,网站内的编码主题多样化,调试功能可运用性强,可谓是非常注重用户体验。这么好的免费刷题网站还不快入手吗,快去注册开启算法百炼成神之路吧!

1、AB13 【模板】拓扑排序

学会使用邻接表解决图论问题,巧妙利用vector容器

题目链接:拓朴排序

ab15 【模板】单源最短路2,# 经典算法的C++实现,算法,图论,数据结构

1.1、解题思路

解决拓扑排序之前要先认识什么是拓扑排序:

对一个有向无环图(Directed Acyclic Graph简称DAG)图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点uv,若边<u,v>∈E(G)则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序

解决步骤:

  1. 使用邻接表将顶点联系起来,辅助数组inDegree表示每个顶点的入度。
  2. 借助队列和计数器变量来判断该有向图是否有环:
    • 将入度为零的顶点入队(也就是拓扑图第一个顶点)
    • 取队首,遍历与之相邻的顶点,若该顶点入度减一后为零就将其入队
    • 只要队列非空就循环操作,计算器循环加一,与顶点数比较是否相等
  3. 本题末尾也不能输出空格,因此输出拓扑序列时要加限制条件

1.2、代码实现与注释

本题源码:

#include<iostream>
#include<vector>
#include<queue>
#define M 200001
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> adjList[M]; // 模拟邻接表
    int inDegree[M] = { 0 };// 记录每个顶点的入度
    int a, b;
    for (int i = 0; i < m; i++) {
        cin >> a >> b;
        adjList[a].push_back(b);
        inDegree[b]++;
    }
    queue<int> que; // 将初始入度为零的顶点入队
    for (int i = 1; i <= n; i++) {
        if (inDegree[i] == 0)
            que.push(i);
    }

    int cnt = 0; // 用来计数,判断改图是否有环
    vector<int> res; // 用来输出顶点序列
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        res.push_back(u);
        for (int i = 0; i < adjList[u].size(); i++) { // 遍历u的相邻顶点
            int v = adjList[u][i];
            if (--inDegree[v] == 0)
                que.push(v);
        }
        cnt++;
    }
    // 若计数器与顶点数相同则图无环,存在拓扑排序
    if (cnt == n) {
        for (int i = 0; i < res.size(); i++) {
            cout << res[i];
            // 限制输出空格的条件
            if (i != res.size() - 1) {
                cout << " ";
            }
        }
    } else {
        cout << -1;
    }
    return 0;
}

重要注释:

  • adjList数组是vector类型的,用来模拟邻接表
    • 使用每个元素为一个数组的vector容器模拟邻接表进行建图
    • vector[a]所对应的数组中存储着该顶点所指向的其他顶点
    • inDegree数组代表每一个顶点的入度情况
  • 使用一个队列,初始时将所有入度为0的顶点全部入队,之后采用BFS的思想:
    • 依次取出队头元素并存入结果数组中,然后在邻接表中遍历该队头元素所指向的其他顶点
    • 将这些顶点的入度全部减一,若减一后某顶点的入度变为0,则将该顶点进行入队操作,
      重复此步骤直至队列为空为止。
  • 设置一个用于判断图中是否存在环(是否可以得到拓扑序列)的计数器,在弹出队头元素后要将计数器加一,最后队列为空后,若计数器的值与顶点数相同,则说明图不存在环,可以得到拓扑序列。

2、AB14 最小生成树

题目链接:最小生成树

ab15 【模板】单源最短路2,# 经典算法的C++实现,算法,图论,数据结构

2.1、解题思路

本题要求在最小花费下将 n 户人家连接起来,很显然是最小生成树的问题,我采用prim算法:

  1. 将二维数组cost按权升序排序,那么cost[0][2]就是最小的一个权值
  2. 将连接这条边的两个顶点放入unordered_set容器:
    • unordered_set 容器的元素是无序的,可以用来给顶点去重
    • 内置的 find 方法也很好用
  3. 接下来遍历cost二维数组,直到所有顶点全部放入容器中,遍历结束
  4. 将遍历中权值的和返回,程序结束

2.2、代码实现与注释

本题源码:

class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 返回最小的花费代价使得这n户人家连接起来
     * @param n int n户人家的村庄
     * @param m int m条路
     * @param cost intvector<vector<>> 一维3个参数,表示连接1个村庄到另外1个村庄的花费的代价
     * @return int
     */

    // 自定义排序规则:按权递增
    static bool cmp(vector<int>& x, vector<int>& y) {
        return x[2] < y[2];
    }
    int miniSpanningTree(int n, int m, vector<vector<int> >& cost) {
        unordered_set<int> points; // 记录不重复的点
        int res = 0;
        sort(cost.begin(), cost.end(), cmp);
        res += cost[0][2]; // 此时res 为最小权值
        // 将最小边加入
        points.insert(cost[0][0]);
        points.insert(cost[0][1]);
        while (1) {
            if (points.size() == n)
                break; // 所有的点连同后退出循环
            // 遍历剩余的边
            for (auto it = cost.begin(); it != cost.end(); it++) {
                // 如果边仅有一个点在集合内就加入
                if ((points.find((*it)[0]) != points.end() &&
                    points.find((*it)[1]) == points.end()) ||
                    (points.find((*it)[1]) != points.end() &&
                    points.find((*it)[0]) == points.end()))
                    {
                        res += (*it)[2];
                        points.insert((*it)[0]);
                        points.insert((*it)[1]);
                        cost.erase(it); // 删除该边
                        break;
                    }
            }
        }
        return res;
    }
};

重要注释:

  • cmp 是自定义的一个按权递增的排序函数,配合sort函数来将cost排序
    • 相关知识点可以参考我的博文:自定义排序规则
  • auto关键字可以自动推导表达式类型,在这里就相当于vector<vector<int>>::iterator
  • if的条件很长,但其实就是将有且仅有一个顶点在points中的边找到:
    • 获取该边的权值并求和,将另一顶点插入到points
    • 将该边删除,重新遍历cost,直到全部顶点被插入到points
  • 最终的 res 就是该题的结果,即最小花费。

3、AB15 单源最短路2

题目链接:单源最短路2

ab15 【模板】单源最短路2,# 经典算法的C++实现,算法,图论,数据结构ab15 【模板】单源最短路2,# 经典算法的C++实现,算法,图论,数据结构

3.1、解题思路

使用Dijkstra算法(即不断从未处理集合中找当前距离源点最近的顶点以添加到已处理集合中,直至未处理集合为空的算法思想)

  1. 对于无向图,采用邻接矩阵表示点与点的连接关系以及距离
  2. 使用数组dist记录每个顶点与源点的距离:
    • 本题中就是记录顶点1与其他顶点的距离
  3. 用一个布尔类型的数组记录顶点的处理情况:
    • 初始状态全部设为false,一经处理就设为true
  4. 最终dist[n]就是顶点n到源点的最短距离

3.2、代码实现与注释

本题源码

#include<iostream>
#include<climits> // 使用INT_MAX所需要引入的头文件
using namespace std;
const int N = 5000; // 注意题干,图的点数是固定值5000

int main() {

    int G[N + 1][N + 1]; // 用于模拟邻接矩阵进行建图
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            G[i][j] = INT_MAX; // 先将邻接矩阵全部初始化为无穷大
        }
    }
    int n, m;
    cin >> n >> m;
    int u, v, w;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        G[u][v] = w;
        G[v][u] = w; // 需要关于主对角线对称,因此两边都需要存储
    }
    int dist[N + 1]; // 用于存储每个顶点当前与源点的最短距离

    bool flag[N + 1]; // 用于记录每个顶点是否已经完成与源点最短距离的计算处理

    for (int i = 1; i <= N; i++) {
        dist[i] = G[1][i]; // 初始设置为邻接矩阵中源点所在 行 的权值
        flag[i] = false;
    }
    
    dist[1] = 0;
    flag[1] = true; // 将源点加入已处理集合
    
    for (int i = 2; i <= N; i++) {
        int tmp = INT_MAX, index = 1;
        for (int j = 1; j <= N; j++) { // 遍历寻找与源点的最短距离
            if (flag[j] == false && dist[j] < tmp) {
                tmp = dist[j];
                index = j;
            }
        }
        if (index != 1) {
            flag[index] = true; // 找到后将其加入已处理集合
        }
        for (int j = 2; j <= N; j++) {
            if (flag[j] == false && G[index][j] != INT_MAX) {
                if (G[index][j] + dist[index] < dist[j]) {
                    dist[j] = G[index][j] + dist[index]; // 更新最短路径
                }
            }
        }
    }
    if (dist[n] != INT_MAX) {
        cout << dist[n];
    } else {
        cout << -1;
    }
    return 0;
}

重要注释:

  • 二维数组G是具化出的邻接矩阵,将元素值全部初始化为无穷大:INT_MAX
  • u,v记录两个顶点,w记录顶点间距离,全部存入G
  • dist数组用来存放各顶点到源点的距离,flag数组记录顶点是否被处理过
  • index代表着与源点连接且距离最短的未经处理的顶点:
    • 随后将该顶点设为已处理
  • 然后index寻找与之相连且未经处理的顶点:
    • dist[index]index到源点的最短距离,如果加上与之相连的顶点的距离较小,那么就更新最短路径
  • 最终的dist数组的值就是各点到源点的最短路径

当自己动手去做的时候才发现原来我以为的难题,不过如此,将这份自信传达给大家,奋斗下去!文章来源地址https://www.toymoban.com/news/detail-780658.html

到了这里,关于【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【图论】单源最短路

    算法提高课笔记 最短路问题可以分为以下两类: 边权非负——朴素Dijkstra、堆优化Dijkstra 有负权边——Bellman-Ford、SPFA 热浪 原题链接 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!! 他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品

    2024年02月14日
    浏览(35)
  • 【图论】单源最短路问题

    Dijkstra算法是一种单源最短路径算法,用于找出图中从一个源点到其他所有点的最短路径。该算法的原理是采用贪心策略,每次将距离源点最近的点加入到已确定最短路径的集合中,并更新其它节点的距离。具体实现过程如下: 初始化距离数组dist[],源点距离为0,其余点距离

    2024年02月13日
    浏览(35)
  • 【算法入门&搜索法】走迷宫|单源最短路径1

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(58)
  • 【图论 单源最短路】100276. 最短路径中的边

    单源最短路 图论知识汇总 给你一个 n 个节点的无向带权图,节点编号为 0 到 n - 1 。图中总共有 m 条边,用二维数组 edges 表示,其中 edges[i] = [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。 对于节点 0 为出发点,节点 n - 1 为结束点的所有最短路,你需要返回一个长度

    2024年04月22日
    浏览(28)
  • 【单源最短路 图论】882. 细分图中的可到达节点

    视频算法专题 单源最短路 图论 给你一个无向图(原始图),图中有 n 个节点,编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。 图用由边组成的二维数组 edges 表示,其中 edges[i] = [ui, vi, cnti] 表示原始图中节点 ui 和 vi 之间存在

    2024年04月10日
    浏览(36)
  • 第三章 图论 No.1单源最短路及其综合应用

    做乘法的最短路时,若权值=0,只能用spfa来做,相等于加法中的负权边 1129. 热浪 1129. 热浪 - AcWing题库 单源最短路,稀疏图,用堆优化Dijkstra即可,就是板子套了个背景 debug:由于是无向图,边的数量要开两倍。但是 w[N] 没改,debug了很久 所以 e[M], ne[M], w[M] ,只有 h[N] ,其他

    2024年02月14日
    浏览(38)
  • 第三章 图论 No.2单源最短路之虚拟源点,状压最短路与最短路次短路条数

    dp是特殊的最短路,是无环图(拓扑图)上的最短路问题 1137. 选择最佳线路 1137. 选择最佳线路 - AcWing题库 对于每组测试数据,该重置的数据要重置,我没有重置idx,导致TLE 处理反向建图,还有一种扩展做法:虚拟源点 设置虚拟源点,与每个起点之间连接边权为0的边 原问题

    2024年02月14日
    浏览(41)
  • 【算法】单源最短路径算法——Dijkstra算法

    迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。这是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用 贪心算法 的策略, 每次遍历到始点距离最

    2024年02月05日
    浏览(38)
  • 数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)

    目录 问题分类  无权图单源最短路径算法 思路 伪代码 时间复杂度 代码实现(C语言) 有权图单源最短路径算法 Dijkstra(迪杰斯特拉)算法 伪代码  时间复杂度  代码实现(C语言) 多源最短路径算法 两种方法 Floyd算法 代码实现(C语言) 最短路径问题的抽象 在网络中,求

    2024年02月08日
    浏览(50)
  • C++算法:单源最短路径Dijkstra

    如果你有一份北京地图,想从中关村走到三元桥,那么怎样能找出实现这一目的的最短路径呢?一种可能的方法就是将这两点之间所有的路线都找出来,然后求出每条路线的距离,找出最短的路线。但是仔细想想我们就会发现这种办法几乎是不可行的,因为这样的路线太多了,

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包