题目链接

743. 网络延迟时间

题目描述

有 N 个网络节点,标记为 1 到 N。

给定一个列表 times,表示信号经过有向边的传递时间。 times[i] = (u, v, w),其中 u 是源节点,v 是目标节点, w 是一个信号从源节点传递到目标节点的时间。

现在,我们从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1。

样例

输入:times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2输出:2

数据范围

N 的范围在 [1, 100] 之间。
K 的范围在 [1, N] 之间。
times 的长度在 [1, 6000] 之间。
所有的边 times[i] = (u, v, w) 都有 1 <= u, v <= N 且 0 <= w <= 100。

算法

图论的题目一般是 3 步,建图 -> 图论算法 -> 后处理。

本题也是一样的思路:先建邻接表,然后求单源最短路,然后根据找到的各个点的最短路,找到从起始点出发可以到达的最远的距离。

一般比较难的题目难点都是在建图这一步。有的是非常隐晦的图论问题,想不到建图处理,例如 127. 单词接龙;有的是建图过程中 corner case 非常多,很容易漏,例如 444. 序列重建。成功完成建图之后(一般是建邻接表比较多),之后的图论算法就比较模板化了。

当边的权都相同时,求最短路直接一趟 BFS就可以,典型题目 1091. 二进制矩阵中的最短路径。当边权不同的时候有以下几种。

dijkstra 数组实现: O(E + V^2),有贪心思想,不能处理负权
dijkstra 堆实现: O(ElogV + VlogV),有贪心思想,不能处理负权
bellman ford: O(VE), 可以处理负权,可以检测负环
spfa: O(VE), bellman ford 的优化, 可以处理负权,可以检测负环
floyd: O(V^3) 可以把所有点之间的最短路径求出,如果求多源最短路,时间复杂度可摊销

如果没有负权的话,一般使用 dijkstra 最好。

代码(c++)

代码框架就是 建立邻接表 -> 单源最短路算法 -> 后处理

单源最短路算法的 5 种算法都是模板,对应的实现在引申部分

class Solution {public:int networkDelayTime(vector<vector<int>>& times, int N, int K) {vector<vector<vector<int> > > g(N + 1);for(vector<int> &edge: times)g[edge[0]].push_back({edge[1], edge[2]});// 求带权邻接表的最短路vector<int> d = dijkstra_array(g, K, N);// vector<int> d = dijkstra_heap(g, K, N);// vector<int> d = bellman_ford(g, K, N);// vector<int> d = spfa(g, K, N);// vector<int> d = floyd(g, K, N);int res = 0;for(int i = 1; i <= N; i++){if(d[i] == -1)return -1;res = max(res, d[i]);}return res;}
};

引申: 权邻接表的单源最短路模板代码

接口:

// g 是建好的邻接表,start 是起始点,N 是节点个数; 返回各个点到 start 的最短路径
vector<int> shortest_path(vector<vector<vector<int> > >& g, int start, int N);

5个算法的接口都一样,下面是对应的模板代码,原理部分没有展开。

如果能够将问题转化成模板问题,再套模板就轻松愉快了。这个有点类似下棋或者炒菜的背谱,只会背谱肯定是不好的,但是背谱可以快速下出挑不出什么毛病的棋,快速炒出80分的菜。

1. dijkstra 数组实现

vector<int> dijkstra_array(vector<vector<vector<int> > >& g, int start, int N)
{// dijkstra 数组实现 O(E + V^2)// 不能有负权// 存放 start 到各个点的最短路径vector<int> d(N + 1, -1);d[start] = 0;// 记录是否找到 start 到该点的最短路径vector<bool> visited(N + 1, false);visited[start] = true;// 初始化 start 到各个点的距离for(vector<int> son: g[start])d[son[0]] = son[1];for(int cnt = 1; cnt <= N - 1; ++cnt){int min_val = INT_MAX / 2, min_idx = 0;// 遍历所有节点,找到离 start 最近的节点for(int i = 1; i <= N; ++i){if(d[i] != -1 && !visited[i] && d[i] < min_val){min_idx = i;min_val = d[i];}}// 标记离 start 最近距离节点已经找到visited[min_idx] = true;// 根据刚刚找到的距离 start 最短的节点,// 通过该节点更新 start 与其它节点的距离for(vector<int> son: g[min_idx]){if(d[son[0]] != -1) // 之前路径与当前更新路径的最小值d[son[0]] = min(d[son[0]], min_val + son[1]);else // 该节点第一次访问,直接更新d[son[0]] = min_val + son[1];}}return d;
}

2. dijkstra 堆实现

vector<int> dijkstra_heap(vector<vector<vector<int> > >& g, int start, int N)
{// dijkstra 堆实现 O(ElogV + VlogV)// 不能有负权// 存放 start 到各个点的最短路径vector<int> d(N + 1, INT_MAX / 2);d[start] = 0;priority_queue<vector<int>, vector<vector<int> >, Cmp> pq; // 队列元素 (节点编号,到 start 的距离)pq.push({start, 0});while(!pq.empty()){vector<int> cur = pq.top();pq.pop();if(d[cur[0]] < cur[1]) continue;for(vector<int> son: g[cur[0]]){if(d[son[0]] <= d[cur[0]] + son[1]) continue;d[son[0]] = d[cur[0]] + son[1];pq.push({son[0], d[son[0]]});}}return d;
}struct Cmp
{bool operator() (const vector<int>& item1, const vector<int>& item2){return item1[1] > item2[1]; // 最小堆}
};

3. bellman ford

松弛操作,它的原理是著名的定理:“三角形两边之和大于第三边”

vector<int> bellman_ford(vector<vector<vector<int> > >& g, int start, int N)
{// bellman ford  O(VE)// 可以检测负环vector<int> d(N + 1, -1);d[start] = 0;// 进行 N - 1 轮松弛// 因为任意两点之间最短路最多包含 N - 1 条边for(int cnt = 1; cnt <= N - 1; ++cnt){// u: 源节点,v: 子节点, w: uv 的权for(int u = 1; u <= N; ++u){if(d[u] == -1) continue;for(vector<int> &son: g[u]){int v = son[0], w = son[1];// 判断能否通过 u -> v 缩短 d[v] (松弛)if(d[u] + w < d[v] || d[v] == -1)d[v] = d[u] + w;}}}/* 可以检测负环for(int u = 1; u <= N; ++u){for(vector<int> &son: g[u]){int v = son[0], w = son[1];if(d[u] + w < d[v])// 有负环}}*/return d;
}

4. spfa

SPFA算法的基本思想:在Bellman-Ford算法中,很多松弛操作其实都是没有必要的,例如对于一条从 x 到 y 的边,如果连 x 都还没被松弛,那 y 肯定也还不能被 x 松弛。用一个队列来存储已经被松弛过的点,然后用队列里的点去松弛其他点,就可以避免用一个还没有被松弛的点去松弛另外的点。

vector<int> spfa(vector<vector<vector<int> > >& g, int start, int N)
{// 与 bellman ford 相同, O(VE)// 可以检测负环vector<int> d(N + 1, -1);d[start] = 0;queue<int, list<int> > q;q.push(start);// 记录每个点到 start 的节点个数vector<int> cnt(N + 1, 0);cnt[start] = 1;while(!q.empty()){int cur = q.front();q.pop();for(vector<int> &son: g[cur]){if(d[son[0]] == -1 || d[son[0]] > d[cur] + son[1]){cnt[son[0]] = cnt[cur] + 1;if(cnt[son[0]] > N) return d; // 若 son 到 start 的节点个数大于 N 了说明有负环// 当最短距离发生变化且不在队列中时,将该节点加入队列d[son[0]] = d[cur] + son[1];q.push(son[0]);}}}return d;
}

5. floyd

vector<int> floyd(vector<vector<vector<int> > >& g, int start, int N)
{// floyd 需要邻接矩阵 O(V^3)// 可以做多源最短路vector<vector<int> > adj_matrix(N + 1, vector<int>(N + 1, -1));for(int i = 1; i <= N; ++i)adj_matrix[i][i] = 0;for(int u = 1; u <= N; ++u)for(vector<int> son: g[u])adj_matrix[u][son[0]] = son[1];// 遍历所有节点,其中 k 是用于松弛的点for(int k = 1; k <= N; ++k)for(int i = 1; i <= N; ++i)for(int j = 1; j <= N; ++j)// 使用 k 松弛 i -> j 的最短路径if(adj_matrix[i][k] != -1 && adj_matrix[k][j] != -1){if(adj_matrix[i][j] != -1)adj_matrix[i][j] = min(adj_matrix[i][j], adj_matrix[i][k] + adj_matrix[k][j]);elseadj_matrix[i][j] = adj_matrix[i][k] + adj_matrix[k][j];}vector<int> d(N + 1, -1);for(int i = 1; i <= N; ++i)d[i] = adj_matrix[start][i];return d;
}

邻接矩阵和邻接表_[力扣743] 带权邻接表的单源最短路相关推荐

  1. dijkstra邻接表_[力扣743] 带权邻接表的单源最短路

    题目链接 743. 网络延迟时间 题目描述 有 N 个网络节点,标记为 1 到 N. 给定一个列表 times,表示信号经过有向边的传递时间. times[i] = (u, v, w),其中 u 是源 ...

  2. ❤️导图整理数组6:四数组的四数之和,详解Counter类实现哈希表计数,力扣454❤️

    此专栏文章是对力扣上算法题目各种方法的总结和归纳, 整理出最重要的思路和知识重点并以思维导图形式呈现, 当然也会加上我对导图的详解. 目的是为了更方便快捷的记忆和回忆算法重点(不用每次都重复看题解), ...

  3. SPFA求单源最短路(邻接表)

    SPFA 介绍 SPFA(Shortest Path Faster Algorithm)(队列优化)算法是求单源最短路径的一种算法,在Bellman-ford算法的基础上加上一个队列优化,减少了冗余的 ...

  4. 邻接表实现的有向带权图 及 图算法(C++)

    邻接表实现的有向带权图 相关概念 声明和定义 实现 1. 构造函数 2. 析构函数 3. 深度优先遍历 4. 广度优先遍历 5. 获取顶点在邻接表中对应的下标 6. 添加顶点 7. 移除顶点 8. 添 ...

  5. MySQL中改变相邻学生座位_力扣——换座位(数据库的题

    小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id. 其中纵列的 id 是连续递增的 小美想改变相邻俩学生的座位. 你能不能帮她写一个 SQL q ...

  6. MYSQL将一个人的性别改为女_力扣数据库题目627变更性别

    力扣数据库题目627变更性别 给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值.交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然).要求只使用一个更新 ...

  7. 单调不减序列查询第一个大于等于_[力扣84,85] 单调栈

    题目链接 84. 柱状图中最大的矩形 85. 最大矩形 题目描述-84 给定 n 个非负整数,用来表示柱状图中各个柱子的高度.每个柱子彼此相邻,且宽度为 1 . 求在该柱状图中,能够勾勒出来的矩形的最 ...

  8. 台安变频器n2按键说明_力扣 925. 长按键入

    925. 长按键入 题目描述 你的朋友正在使用键盘输入他的名字 name.偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次. 你将会检查键盘输入的字符 typed.如果它对应 ...

  9. 力扣Java编译器_力扣--设计单链表

    在链表类中实现这些功能: get(index):获取链表中第 index 个节点的值.如果索引无效,则返回-1. addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点.插入 ...

最新文章

  1. iOS开发UIlabel篇:iOS 10 3 Label设置的中划线突然失效了
  2. PHP生成PDF完美支持中文,解决TCPDF乱码
  3. BZOJ 4810 [Ynoi2017]由乃的玉米田 ——Bitset 莫队算法
  4. 菜鸟学Java(二十一)——如何更好的进行单元测试——JUnit
  5. python2和python3的不同点_Django python2和python3的区别
  6. 信用卡的3种分期模式全面比较
  7. waveOutGetDevCaps - 查询输出设备的性能
  8. jdba访问mysql_mysql连接出现问题记录
  9. input readonly 光标显示问题
  10. memcache 缓存命中率   状态查询
  11. 马斯克提出以430亿收购推特 推特考虑用毒丸来阻止其增持股份
  12. 决策树算法python实现_决策树之python实现ID3算法(例子)
  13. 【软件测试】单元测试不属于动态测试
  14. android 7.0 截图,Android,_7.0系统拍照后,使用系统截图功能,截图保存时崩溃如何解决,Android - phpStudy...
  15. Windows XP SP2 重置 WinSock:netsh winsock reset catalog
  16. 编辑器使用方法 1. 下载编辑器 下载 KindEditor 最新版本,下载之后打开 examples/index.html 就可以看到演示。 下载页面: http://www.kindsoft.
  17. 【愚公系列】2022年01月 Django商城项目18-用户中心-密码修改功能页面设计
  18. python猜拳小游戏_Python入门猜拳小游戏
  19. java计算机毕业设计体检系统源码+系统+数据库+lw文档
  20. 计算机打印状态错误,打印机出现错误状态是怎么回事,教您解决办法

热门文章

  1. 解决PendingIntent传递参数为空的问题
  2. Android Studio如何快速生成get,set,tostring,构造函数
  3. perl里面隐式的继承
  4. 牛客13584 日历中的数字
  5. hadoop伪分布式配置
  6. Leetcode--1014. 最佳观光组合(java)
  7. 【剑指offer】面试题49:丑数
  8. php根据分类生成网址,PHP实现无限极分类生成分类树的方法
  9. wdcp mysql远程_CentOS下WDCP下的MYSQL开启远程连接
  10. 分数怎么在计算机上关,电脑如何在注册表上关闭AutoRun功能