相关题
LeetCode中有向图相关的题的题号为:743,685,684,399,332,310,210
743. 网络延迟时间
有 N 个网络节点,标记为 1 到 N。
给定一个列表 times,表示信号经过有向边的传递时间。 times[i] = (u, v, w),其中 u 是源节点,v 是目标节点, w 是一个信号从源节点传递到目标节点的时间。
现在,我们从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1。
解题思路:本题本质上在考察最短路径算法,最短路径算法有单源最短路,多源最短路,然后还掺杂着有权,无权,有向,无向。单源最短路有Dijkstra算法,贝尔福特慢算法(Bellman-Ford Algorithm),Dijkstra算法是模仿图的广度优先遍历(BFS)写的,类似水滴荡起的纹波、迷宫找路等场景。Dijkstra算法首先维护一个dist数组参与松弛操作(Relaxation),当dist[u]>dist[v]+e[u,v],更新路径值dist[u]=dist[v]+e[u,v],防止重复搜索,用visited数组标记已访问过的节点。此题解法我和Grandyang的解法大致相同,但是我的visited数组作为全局变量时,有一半的test case无法通过,不太理解为什么visited作为局部变量就能AC,我在 考虑这个问题的时候思考的方向是,想象水滴荡起纹波的场景,visited作为局部变量,最直接的表象是防止同一层的节点再次被访问,但是将visited作为全局变量,也能达到这个效果,而且也没有干扰dist[u]的更新(之前干过,将visited数组先于dist[u]关系的判断,导致有些节点当放置靠后的位置访问时,能使dist[u]更小),想到这,思路就短路了,接不上了,日后看到此题再思考╮(╯▽╰)╭
在不完全理解此题解法时,也当记住最短路在应用Dijkstra算法时,将visited数组放在层内判断的一种写法。
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
unordered_map<int, vector<pair<int, int>>> g;
for (auto t : times) {
g[t[0]].push_back({t[1], t[2]});
}
vector<int> dist(N + 1, INT_MAX);
queue<int> q{{K}};
dist[K] = 0;
while (!q.empty()) {
unordered_set<int> visited;
for (int i = q.size(); i > 0; --i) {
int node = q.front(); q.pop();
for (auto t : g[node]) {
if (dist[t.first] > dist[node] + t.second) {
dist[t.first] = dist[node] + t.second;
if (!visited.count(t.first)) {
visited.insert(t.first);
q.push(t.first);
}
}
}
}
}
int res = 0;
for (int i = 1; i <= N; ++i) {
res = max(res, dist[i]);
}
return res == INT_MAX ? -1 : res;
}
};
Bellman-Ford算法近似于暴力解法,暴力美学,针对所有的点,对存在的边都进行遍历一次,对已经连接到K点的u节点进行松弛操作。
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int res = 0;
vector<int> dist(N + 1, INT_MAX);
dist[K] = 0;
for (int i = 1; i < N; ++i) {
for (auto time : times) {
int u = time[0], v = time[1], w = time[2];
if (dist[u] != INT_MAX && dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
}
}
}
for (int i = 1; i <= N; ++i) {
res = max(res, dist[i]);
}
return res == INT_MAX ? -1 : res;
}
};
上面内容时间:2020-05-13 18:53:48
更新时间:2020-8-2 16:38:26
时隔3个月再做此题。
下面采取的是dijkstra算法的标准写法,模板解题,相关的模板可以见这篇博客,关于图的最短路,对于无权图可以用变型的BFS解,层数对应的即是最短路长度(可以把无权图等同成权值为1的有权图来看),但是对于有权图,用BFS水滴纹波来解就不太合适,因为每层向外延展的路径不等价(水纹荡漾开来的径向距离不等价),对于有权图只能采取dijkstra算法(单源最短路),Floyed算法(多源最短路)
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> dist(N + 1, INT_MAX);
vector<vector<pair<int, int>>> e(N + 1);
for (auto &t : times) {
e[t[0]].push_back(make_pair(t[1], t[2]));
}
vector<bool> visited(N + 1, false);
dist[K] = 0;
while (1) {
int mindis = INT_MAX, minidx = -1;
for (int i = 1; i <= N; ++i) {
if (visited[i] == false) {
if (mindis > dist[i]) {
mindis = dist[i];
minidx = i;
}
}
}
if (minidx == -1) break;
visited[minidx] = true;
for (int i = 0; i < e[minidx].size(); ++i) {
int u = e[minidx][i].first, dis = e[minidx][i].second;
if (visited[u] == false) {
if (dist[u] > dist[minidx] + dis) {
dist[u] = dist[minidx] + dis;
}
}
}
}
int res = 0;
for (int i = 1; i <= N; ++i) {
if (dist[i] == INT_MAX) return -1;
res = max(res, dist[i]);
}
return res;
}
};
684. 冗余连接
解题思路: 本题本质上是判断图中是否成环,不过用并查集解题能恰好回避此问题,如果能想到用并查集解此题,估计解题思路基本就出来了,具体的是,在访问边数组的同时构建并查集,若发现某个边对应的两个节点查询到的parent相同,则说明通过之前访问的边已经构建出这两个节点间的通路,而此次访问的是两节点间的直接通路,说明成环。此题的难点是如何通过图的