最短路算法——Dijkstra(C++实现)

写在前面:

由于本人实力尚浅,接触算法没多久,写这篇blog仅仅是想要提升自己对算法的理解,如果各位读者发现什么错误,恳请指正,希望和大家一起进步。

题目:

const int N=510;    

int f[N][N];    //用邻接矩阵来存储图,最开始要初始化为INF(0x3f3f3f3f)
int dist[N];    //用该数组来存储图上顶点到源点的距离,最开始也要初始化为INF
int st[N];      //st是state的缩写,用来表示该顶点是否已经被确定

思路:

  1. 在未被确定的顶点中选出距离源点最近的顶点v,同时将顶点v状态置为已经确定最短路。
  2. 遍历顶点v的所有边,更新与v相连的点到源点的距离。
  3. 循环上面两步,一直迭代n次(因为每迭代一次就会确定一个点,一共有n个点,所以是n次)

代码: 

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
const int N=510;
int n,m,f[N][N],dist[N],st[N];

void dijkstra()
{
    dist[1]=0;      //将源点到源点的距离置为0
    for(int i=0;i<n;i++)        //由于每次确定一个顶点的最短距离,所以迭代n次
    {
        int t=0;        //t表示当前访问的顶点
        for(int j=1;j<=n;j++)       //经过t与每一个未被确定的顶点比较,选出距离源点最近的顶点
            if(!st[j] and (dist[j]<dist[t] or !t))
                t=j;
        for(int j=1;j<=n;j++)       //遍历顶点t所有边,更新其他点到源点的距离
            dist[j]=min(dist[j],dist[t]+f[t][j]);
        st[t]=1;        //将t状态置为已经确定
    }
}

int main()
{
    cin>>n>>m;
    memset(f,0x3f,sizeof f);        //初始化f,dist数组
    memset(dist,0x3f,sizeof dist);
    while(m--)      //读入每一条边,建图
    {
        int a,b,c;
        cin>>a>>b>>c;
        f[a][b]=min(f[a][b],c);
    }
    
    dijkstra();
    if(dist[n]==0x3f3f3f3f)     //如果n到源点的距离为INF,那就说明这个图不是连通图
        cout<<-1<<endl;
    else
        cout<<dist[n]<<endl;
    return 0;
}

总结:

  •  Dijkstra适合用来求解单源汇非负权图的最短路问题,算法的思路其实非常清晰,代码也比较好写。
  • 算法的时间复杂度是O(n^2)
  • Dijkstra的证明过程在这里我就不写了(因为我自己到现在也没怎么搞明白),各位读者可以自行Google。 

拓展: 

还有一种堆优化的Dijkstra算法,其实就是用小根堆(优先队列)来维护代更新的顶点的顺序,保证每次迭代中,不需要在去遍历所有顶点来找到最近的顶点。时间复杂度是O(mlogn),m是边数,n是顶点数。

#include<iostream>
#include<queue>
#include<cstring>

using namespace std;
typedef pair<int,int> PII;
const int N=150010;
int n,m;
int h[N],e[N],ne[N],w[N],st[N],d[N],idx;

void add(int a,int b,int c)     //邻接表添加一条边
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void dijkstra()
{
    memset(d,0x3f,sizeof d);
    d[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;      //构建小根堆
    heap.push({0,1});       //将源点压入堆
    while(!heap.empty())        //只要堆不为空,就一直迭代
    {
        auto t=heap.top();      //取出堆顶
        heap.pop();
        int dist=t.first,pos=t.second;      //dist表示当前顶点距离远点的距离,pos表示当前顶点的编号
        if(st[pos])         //如果该顶点已经被确定,就跳过本次循环
            continue;
        for(int i=h[pos];i!=-1;i=ne[i])     //遍历所有与pos相连的边
        {
            int j=e[i];
            if(d[j]>dist+w[i])      //如果可以更新,就更新并且将该顶点压入堆中
            {
                d[j]=min(d[j],dist+w[i]);
                heap.push({d[j],j});
            }
        }
        st[pos]=1;
    }
}

int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    
    dijkstra();
    if(d[n]==0x3f3f3f3f)
        cout<<-1<<endl;
    else
        cout<<d[n]<<endl;
    return 0;
}

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是C++实现Dijkstra算法的示例代码: ```c++ #include <iostream> #include <vector> #include <queue> #include <climits> using namespace std; #define INF INT_MAX // 定义无穷大 // 定义边的结构体 struct Edge { int to; // 边的终点 int cost; // 边的权值 }; // 定义图的结构体 struct Graph { int V; // 图的顶点数 vector<vector<Edge>> adj; // 邻接表 }; // Dijkstra算法 vector<int> dijkstra(const Graph& g, int s) { vector<int> dist(g.V, INF); // 存储起点到各点的最短距离 dist[s] = 0; // 起点到自己的距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que; // 存储已经确定最短路的节点 vector<bool> used(g.V, false); que.push(make_pair(0, s)); while (!que.empty()) { pair<int, int> p = que.top(); que.pop(); int v = p.second; if (used[v]) { continue; } used[v] = true; for (auto& e : g.adj[v]) { if (dist[e.to] > dist[v] + e.cost) { dist[e.to] = dist[v] + e.cost; que.push(make_pair(dist[e.to], e.to)); } } } return dist; } int main() { // 用邻接表表示下面这个图 // 1 // 7 / \3 // / \ // 0-----2 // 5\ /2 // \ / // 3 Graph g = { 4, vector<vector<Edge>>(4) }; g.adj[0].push_back(Edge{ 1, 7 }); g.adj[0].push_back(Edge{ 3, 5 }); g.adj[1].push_back(Edge{ 0, 7 }); g.adj[1].push_back(Edge{ 2, 3 }); g.adj[2].push_back(Edge{ 0, 3 }); g.adj[2].push_back(Edge{ 1, 3 }); g.adj[2].push_back(Edge{ 3, 2 }); g.adj[3].push_back(Edge{ 0, 5 }); g.adj[3].push_back(Edge{ 2, 2 }); vector<int> dist = dijkstra(g, 0); for (int i = 0; i < dist.size(); i++) { cout << "0 -> " << i << ": " << dist[i] << endl; } return 0; } ``` 注释已经很详细了,这里简单解释一下。首先定义了边的结构体和图的结构体,存储边和邻接表。然后是Dijkstra算法实现,使用了优先队列来存储已经确定最短路的节点,每次取出队列中距离起点最近的点进行扩展,如果扩展后可以更新到达其它点的距离,则更新距离。最后返回起点到各点的最短距离。最后在main函数中构造了一个图来测试Dijkstra算法的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值