LeetCode第 743 题:网络延迟时间(C++)

743. 网络延迟时间 - 力扣(LeetCode)
在这里插入图片描述

计算的是要是所有节点都接收到信号经历的时长,所以必然是需要遍历整个图的,并且遍历的时候需要记录每个节点接收到信号经历的时长,时长也就是该节点到起始节点 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;
    }
};
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值