Dijkstra算法

57 篇文章 63 订阅

Dijkstra算法 是一种单源点最短路算法求出一个点到其他所有点的最短路。

给你这样的一个图,需要求出1号点到其他点的最短距离是多少。

 

首先我们开一个数组 d[N],d[x] 代表着从起点出发到x点的距离是多少。

开一个数组vis[N], vis[x]数组代表着某个点d[x]是不是成为定值,不会再变小了。

然后我们在开一个数组,edge[N][N],edge[a][b] 代表着从a点走到b的路程是多少。

如果不存在 a->b的这条边,那么就将他设置为-1。

更新d数组的条件:d[a] > d[b] + edge[b][a] 的时候更新d[a]的值。

一开始我们将所有距离设置都设置 inf ( inf 意为无穷大)。

当然d[s] = 0;

所以对于刚开始的数组d[]来说他的值应该为

图的状态应该是

现在我们从1号点出发,图上存在一个 1->3的边 距离为 10  存在一个1->2的边 距离为20。

那么d[3] =  min(d[3], d[1] + edge[1][3]) d[2] = min(d[2], d[1] + edge[1][2])

 

d数组即被更新成

其中vis[1] = 1,即这个点不会的距离不会在变小了。

我们扫一遍d数组,跳过vis[x] == 1的点,找到d[x]最小的点,通过上面的那个数组我们可以发现,这个点是3,

我们把 vis[3] = 1, 然后再通过3号点出发更新d[].

   d数组的值为

 

然后我们继续找到d[x]最小的且没被标记过的点  由上表可知是点2

我们标记点2,然后再用2号点出发,看看有没有点的距离可以被更新成更小的。

当我们走完2号点的边的时候,图就会变成

d数组的结果为

我们继续找到d[x]最小的且没被标记过的点  由上表可知是点4

我们先标记4号点,然后通过点4出发,然后看一下出4号点出发,有没有点的d会被更新成更小的值

 

 d数组的结果为

最后没标记过的点只有5了, 我们从5号点出发,看看有没有点会继续被更新。

我们得到最后的图就变成了

d数组最后就被更新成了

这样我们就进行完了dijkstra算法。

从原点出发到各个点的最短路径是多少就求出来了。

 

假设 d[a] < d[b] , 并且存在edge[a][b] , 那么因为边edge[a][b] > 0,那么不可能通过 b 点去更新 a 点, 只可能从a点出发然后到b点,使得d[b]的更小。

因为更新的条件是  d[b] > d[a] + edge[a][b]。 所以只有从d[]更小的点出发才有可能使得别的点更小。

总结下来的话,就是从原点出发,每次都选出当前距离里原点最近的点(跳过标记过的点)x,然后从x点出发,遍历x点的所有边,看一下是不是存在别的点可以通过点x往外走,使得原点到目标点的距离更小,并且标记一下点x,下次不会再选择x,因为x已经是最小的了。

每次都确立一个点,确立完一个点后需要其访问所有点去更新距离,总有由n个点

所以 时间复杂度是 n * 2n

https://www.cnblogs.com/MingSD/p/9740471.html

Dijkstra基本模板

void dijkstra(int x)  //x为起点,使用迪杰斯特拉查找起点到其他任意点的最短路径
{
    for(int i = 1; i <= n; i++) dis[i] = w[x][i];  //dis[i]数组存储从x到i的最短路(迪杰斯特拉跑完之前不一定是最终解)
    //w[i][j]数组存储从i到j的路径长度(这里用的邻接矩阵存储,也可以换成前向星等)
    b[x] = 1; //b[i]数组存储此点是否被访问过
    dis[x] = 0;
    for(int i = 1; i <= n - 1; i++)
      {
        minl = maxx;
        k = 0;
        for(int j = 1; j <= n; j++)  //查找可以更新的点
          if((!b[j]) && (dis[j] < minl))
            {
                minl = dis[j];
                k = j;
            }
        if(k == 0) break;  //这一句也可以换成if(k == y) break;表示已经更新完从x到y的最短路
        b[k] = true;  //更新k点,把k标记为已确定最短路径
        for(int j = 1; j <= n; j++)  //更新与k相连的每个未确定最短路径的顶点j
          if(dis[k] + w[k][j] < dis[j])
            dis[j] = dis[k] + w[k][j]; //可以加上pre[j] = k用来记录前驱节点
      }
}

 

加入堆优化

 

 

 

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式: 
第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式: 
一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647) 
输入输出样例 
输入样例#1:

4 6 1 
1 2 2 
2 3 2 
2 4 1 
1 3 5 
3 4 3 
1 4 4

输出样例#1:

0 2 4 3

版本一(邻接矩阵存图)

#include<vector>
#include<map>
#include<queue>
#include<algorithm>
#include<set>
#include<iostream>
#include<cstdio>
#include<list>
#include<string>
#include<cmath>
#include<cstring>
#include<stack>
using namespace std;
struct ha//小根堆
{
    int x; 
    int d;//节点编号 
    bool operator < (const ha &a) const 
    {
        return x > a.x;
    }
}; 
struct haha//小根堆
{
    int x; 
    int d;//节点编号
}; 
ha e;
haha ee;
int d[1000010];
priority_queue <ha> q;
int n, m, s, xx, yy, zz, i;
vector<haha> a[1000000];
int main()
{
    scanf("%d%d%d",&n ,&m ,&s);
    for(i = 1; i <= m; i++)
    {
        scanf("%d%d%d",&xx ,&yy ,&zz);
        ee.d = yy;
        ee.x = zz;
        a[xx].push_back(ee);
    }
    for(i = 1; i <= n; i++) d[i] = 2000003647;
    e.d = s;
    e.x = 0; 
    d[s] = 0;
    q.push(e);//入队 
    while (!q.empty())//队列非空 
    {
        e = q.top(); q.pop();//出队
        int v = e.d;//取出节点编号
        int d1 = e.x;
        if (d[v] < d1) continue;//说明在这个点再此之后又入队了
        //此次出队的并不是s到这个点的最短路,
        //所以在这次更新前点v所连的点已经更过一次了 
        //所以后面也不会进行松弛操作 
        int len = a[v].size(); 
        for(i = 0; i < len; i++)
        {
            haha g = a[v][i];
            if ((d[v] + g.x < d[g.d]) || (d[g.d] == -233))//松弛操作 
            {
                d[g.d] = d[v] + g.x;
                e.d = g.d;
                e.x = d[g.d];
                q.push(e);
            }
        }
    }
    for (i=1; i <= n; i++) //输出 
    if (d[i] == 2000003647) cout << 2147483647 << " "; else cout << d[i] << " ";
    return 0;
}


版本二(前向星存图)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int INF = 2147483647;
const int maxn = 10000 + 10;
const int maxm = 500000 + 10;
int n, m, s;
int fir[maxn], nxt[maxm], to[maxm], val[maxm], cnt;
void add_edge(int u, int v, int w) //前向星加边
{
    nxt[++cnt] = fir[u];
    fir[u] = cnt;
    to[cnt] = v;
    val[cnt] = w;
}
struct Node 
{
    int d, id;
    Node(){}
    Node(int d, int id) : d(d), id(id){}
    bool operator < (const Node& rhs) const
      {
        return d > rhs.d;//重载 < 方便堆
      }
};
int dis[maxn], vis[maxn];
void Dijkstra(int s)
{
    for(int i = 1; i <= n; i++) dis[i] = INF;
    dis[s]=0;
    priority_queue<Node> Q;
    Q.push(Node(0,s));
    while(!Q.empty()) 
      {
        Node u = Q.top(); Q.pop();
        if(vis[u.id]) continue;  //若某个点已经被更新到最优,就不用再次更新其他点
        vis[u.id] = 1;
        for(int e = fir[u.id]; e; e = nxt[e]) 
          {
            int v = to[e], w = val[e];
            if(u.d + w < dis[v]) 
              {
                dis[v] = u.d + w;
                Q.push(Node(dis[v],v));
              }
          }
      }
}
int main()
{
    scanf("%d%d%d",&n ,&m ,&s);
    for(int u, v, w, i=0; i < m; i++)
      {
        scanf("%d%d%d",&u ,&v ,&w);
        add_edge(u, v, w);
      }
    Dijkstra(s);
    for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
    return 0;
}



版本三(运用pair存储)
 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<vector>
#include<queue>
#define MAXN 20000
#define INF 2147483647
using namespace std;
typedef pair<int,int> pii;
priority_queue<pii, vector<pii>, greater<pii> > pq;
struct edge
{
    int to;
    int cost;
};
vector<edge> G[MAXN];//g[i]--i to g[i].to cost cost
int n, m, s;
int dis[MAXN];
void dijk(int s)
{
    for(int i = 1; i <= n; i++)
        dis[i] = INF;
    dis[s] = 0;
    pq.push(make_pair(0,s));
   // cout<<dis[s]<<endl;
    while(!pq.empty())
    {
        pii u = pq.top();
        pq.pop();
        int x = u.second; // bian hao
        //cout<<x<<endl;
        for(int i = 0; i < G[x].size(); i++)
        {
            edge e = G[x][i];
            if(dis[e.to] > dis[x] + e.cost)
            {
                dis[e.to] = dis[x] + e.cost;
                pq.push(make_pair(dis[e.to], e.to));
               // cout<<dis[e.to]<<endl;
            }
        }
    }
}
int main()
{
    cin >> n >> m >> s;
    int from, to, cost;
    edge in;
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d%d",&from ,&to ,&cost);
        in.to = to; in.cost = cost;
        G[from].push_back(in);
    }
   // cout<<endl;
    dijk(s);
    for(int i = 1; i <= n; i++)
        printf("%d ", dis[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Starzkg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值