单源最短路径算法【朴素Dijkstra+小根堆优化】

给定结点K和一个有向图edges,我们需要求出K结点到所有结点的最短路径,采用Dijkstra算法,核心思想是贪心,其算法步骤主要为以下:

  1. 将结点分为两类[未确定结点]以及[已确定结点],需要从当前全部[未确定结点]中,找到距离K结点最短的点x
  2. 通过点x更新其他所有点距离源点的最短距离,更新的含义是:用起点到某个点y的距离,和起点到x的最短距离加上x到y的距离,更新为两者中的最小值
  3. 当全部其他点都遍历完成后,一次循环结束,将x标记为[已确定结点],进入下一轮循环,直到所有点都被标记上。

实现的一些细节如下:

  • Dijkstra算法需要存储各个边的边权,稠密图可以采用邻接矩阵来存储,edges[i][j]表示从点i到点j的距离,若两点之间没有边,则初始化为inf,inf的值设置为INT_MAX/2,是因为在更新最短路的时候,防止两个距离相加溢出。
  • 需要记录所有点到源点K的最短距离,使用dist数组来记录,dist[i]表示i结点到源点K的最短距离,源点到源点的距离为0
  • 算法需要标记某一结点是否被标记为[已确定结点],因此用vis数组来标记,当其值为true时说明此节点已经被更新过,已经确定了最短路。

借助题目网络延迟时间,其代码如下:

int networkDelayTime(vector<vector<int>>& times, int n, int k) {
    const int inf=INT_MAX/2;
    vector<vector<int>>edges(n,vector<int>(n,inf)); //邻接矩阵
    for(auto& t:times){ //结点从1到n,为了方便,改成0~n-1
        int x=t[0]-1; //起始结点
        int y=t[1]-1; //终点
        edges[x][y]=t[2]; //距离
    }
    vector<int>dist(n,inf); //距离数组,初始化为inf
    dist[k-1]=0; //K结点到K结点的距离为0
    vector<bool>vis(n,0); //初始所有结点都未被访问
    for(int i=0;i<n;i++){
        //在[未确定结点]中找到距离最短的结点
        int x=-1; 
        for(int y=0;y<n;y++){
            if(!vis[y]&&(x==-1||dist[y]<dist[x])){
                x=y;
            }
        }
        //用该点更新到其他所有点的距离
        vis[x]=true;
        for(int y=0;y<n;y++){
            dist[y]=min(dist[y],dist[x]+edges[x][y]);
        }
    }
    int ans=*max_element(dist.begin(),dist.end()); //找到距离最短的点
    return ans==inf? -1:ans;
}

除了枚举,我们还可以使用一个小根堆来寻找「未确定节点」中与起点距离最近的点。

int networkDelayTime(vector<vector<int>> &times, int n, int k) {
    const int inf = INT_MAX / 2;
    vector<vector<pair<int, int>>> g(n);
    for (auto &t : times) {
        int x = t[0] - 1, y = t[1] - 1;
        g[x].emplace_back(y, t[2]);
    }

    vector<int> dist(n, inf);
    dist[k - 1] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
    q.emplace(0, k - 1);
    while (!q.empty()) {
        auto p = q.top();
        q.pop();
        int time = p.first, x = p.second;
        if (dist[x] < time) { //dist[x]可能已经经过松弛变小了,不必入队列
            continue;
        }
        for (auto &e : g[x]) { //利用最短边进行松弛
            int y = e.first, d = dist[x] + e.second;
            if (d < dist[y]) {
                dist[y] = d;
                q.emplace(d, y);
            }
        }
    }

    int ans = *max_element(dist.begin(), dist.end());
    return ans == inf ? -1 : ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值