计算的是要是所有节点都接收到信号经历的时长,所以必然是需要遍历整个图的,并且遍历的时候需要记录每个节点接收到信号经历的时长,时长也就是该节点到起始节点 K 的最短路径长度。最后的答案就是最大的那个时长。
Dijkstra
那么我们可以使用dijkstra算法计算每个节点到起始节点 K 的最短路径,最后取所有路径中最大的那个即可。
数据结构与算法:44 |最短路径:地图软件如何计算最优出行路径?_zj-CSDN博客
引入两个集合 S 和 U,S 的作用是记录已求出最短路径的顶点(以及相应的最短路径长度,可以使用pair),而U的作用记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
初始情况下,S中只有起点 K(对应最短路径为0,自己到自己);U中是除 K 之外的所有顶点,并且U中顶点的路径是: 起点 K 到该顶点的路径(初始化为 INT_MAX)。
while循环里主要做两件事:
- 找到未处理节点(集合 U)中距离起点路径值最小的节点,同时将该顶点加入集合 S,并从集合 U中移除该顶点。
- 更新 U 中的顶点到起点 K 的记录。
直到遍历完所有顶点,循环结束。
对于寻找最小路径值这一操作,可以使用优先级队列进行优化:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> distance(N+1, INT_MAX);//起点K到每个节点的距离
vector<bool> flags(N+1, false);//记录节点是否已经计算出最短距离
unordered_map<int, vector<pair<int, int>>> adj;//邻接表
//piar<int, int>第一个为距离,第二个为节点编号
for(const auto &v : times) adj[v[0]].push_back({v[2], v[1]});//生成邻接表
//小顶堆,pair<int, int>第一个元素为节点K到该节点的距离,第二个是该节点的编号
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
q.push({0, K});
distance[K] = 0;//自己到自己的距离为0
while(!q.empty()){
auto t = q.top();
q.pop();
int cur = t.second, dis = t.first;//cur表示当前节点,dis表示节点K到当前节点的距离
if(dis == INT_MAX) return -1;//队列头部最小值也为INT_MAX,说明信号无法达到
if(flags[cur]) continue;//之前处理过
flags[cur] = true;
//考虑与当前节点cur相连(指向)的节点,并更新节点K到他们的距离
for(const auto &p : adj[cur]){//adj[cur]映射到vector<pair<int, int>>
//找到更短的距离,需要更新
if(distance[p.second] > dis + p.first){//dis(p.second, k) > dis(k, cur) + dis(cur, p.second),dis代表两者距离
distance[p.second] = dis + p.first;
q.push({distance[p.second], p.second});//{K到该节点的距离,该节点的编号}
}
}
}
int res = *max_element(distance.begin()+1, distance.end());
return res == INT_MAX ? -1 : res;
}
};
如果不使用优先级队列,需要手动寻找最小值,不过思路都是一样的:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<long> distance(N+1, INT_MAX);//起点K到每个顶点的距离,使用long类型
vector<bool> flags(N+1, false);//记录顶点是否已经计算出最短距离
unordered_map<int, vector<pair<int, int>>> adj;//邻接表
//piar<int, int>第一个为距离,第二个为节点编号
for(const auto &v : times) adj[v[0]].push_back({v[2], v[1]});//生成邻接表
distance[K] = 0;//自己到自己的距离为0
for(int i = 0; i < N-1; ++i){//循环次数最多为N-1次(因为起点k已激活)
//遍历所有的顶点,寻找距离起点的距离最小的那个
int t = -1;
for(int j = 1; j <= N; ++j){//编号从1开始,第一位没有使用
if(flags[j]) continue;
if(t == -1 || distance[t] > distance[j]) t = j;
}
flags[t] = true; //该顶点的最短路径已确定
//更新与顶点t相连的节点到起点的距离,其他不相连的顶点不用更新
for(const auto &p : adj[t]){
if(distance[p.second] > distance[t] + p.first){
distance[p.second] = distance[t] + p.first;
}
}
}
int res = *max_element(distance.begin()+1, distance.end());
return res == INT_MAX ? -1 : res;
}
};
优化一下:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> distance(N+1, INT_MAX);//起点K到每个顶点的距离
vector<bool> flags(N+1, false);//记录顶点是否已经计算出最短距离
unordered_map<int, vector<pair<int, int>>> adj;//邻接表
//piar<int, int>第一个为距离,第二个为节点编号
for(const auto &v : times) adj[v[0]].push_back({v[2], v[1]});//生成邻接表
distance[K] = 0;//自己到自己的距离为0
for(int i = 0; i < N; ++i){//循环次数最多为N-1次(因为起点k已激活)
//遍历所有的顶点,寻找距离起点的距离最小的那个
int t = -1;
for(int j = 1; j <= N; ++j){//编号从1开始,第一位没有使用
if(!flags[j] && (t == -1 || distance[t] > distance[j])) t = j;
}
flags[t] = true; //该顶点的最短路径已确定
if(distance[t] == INT_MAX) return -1;//最小值为INT_MAX,说明剩下的顶点都不可能和起点相连,永远接收不到信号
//更新与顶点t相连的节点到起点的距离,其他不相连的顶点不用更新
for(const auto &p : adj[t]){
if(distance[p.second] > distance[t] + p.first){
distance[p.second] = distance[t] + p.first;
}
}
}
return *max_element(distance.begin()+1, distance.end());
}
};
Bellman-Ford
要慢一点,但是代码简单清晰:最短路径(三)—Bellman-Ford算法(解决负权边)_小地盘的诺克萨斯-CSDN博客
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> distance(N+1, INT_MAX/2);//起点K到每个节点的距离
vector<int> backup(N+1);
distance[K] = 0;
for(int i = 0; i < N-1; ++i){//N-1次松弛
backup.assign(distance.begin(), distance.end());//用backup去更新distance
for(const auto &v : times){//枚举每一条边
//注意这儿比较是用的backup,而修改的是distance
//起点k到v[1]的距离 > 起点k到v[0]的距离 + v[0]到v[1]距离
if(distance[v[1]] > backup[v[0]] + v[2]) distance[v[1]] = backup[v[0]] + v[2];
}
if(backup == distance) break;//没有任何松弛操作,提前退出
}
int res = *max_element(distance.begin()+1, distance.end());//因为之前多申请了一个位置,所以一定要注意
return res == INT_MAX/2 ? -1 : res;
}
};