迪杰斯特拉算法

迪杰斯特拉(dijkstra)

单源正权问题求最短路的算法

算法核心思想:

  1. 初始化所有点到源的距离为无穷大的数,为了方便接下来的比较。
  2. 每次找到距离 源(起点) 最近的点(当前最小点),用该点更新其余每个点到源(起点)的距离。
  3. 更新距离的思想:判断该点直接到源的距离,和经过当前最小点到源的距离进行比较,如果小,就更新,依次更新遍历每个点。
  4. 循环遍历出所有的当前最小点。

dijkstra的两种实现:

  1. 朴素版dijkstra(时间复杂度:$O(n^2)$,n为图上的所有点)
  2. 堆优化版的dijkstra(时间复杂度:$O(mlog^n)$,n为图上的所有点,m为边的总数

比较两种时间复杂度,发现:

  1. 朴素版的时间复杂度与图的边数无关,因此,更适于稠密图
  2. 堆优化版的时间复杂度与当n与m近似相等的时候,此时时间复杂度为$O(nlog^n)$,优于朴素版,因此,更适于稀疏图

前缀知识:

稠密图:

图的边数比较多,而点数比较少,用邻接矩阵进行存储数据

邻接矩阵:

通俗为 二维数组  g[N][N]

  1. 当边有权重的时候,则表示权重
  2. 当边无权重的时候,则表示两点是否相连
稀疏图:

图的点数比较多,而边数比较少,用邻接表进行存储

邻接表:

可以理解为数组实现的链表,区别,多个头节点,h[N]

加边方式:

int h[N],e[N],ne[N],idx,w[N];  //h为头节点,e为当前的点,ne为对下一个点的索引,w为权重
memset(h,-1,sizeof h)  //初始化h,保证h最后一个节点指向-1
void add(int a,int b,int c)  //a为出发点,b为指向的点,c为这条边权重
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

如果为双向边,则add(a,b,c),add(b,a,c)

朴素版实现:

#include<bits/stdc++.h>
using namespace std;
const int N=550;
int g[N][N];  //邻接矩阵
int dis[N];  //表示到源的距离
int n,m;  
bool is_used[N];  //判断当前点是否已经是最小距离点
int dijkstra()
{
    dis[1]=0;  //初始1的点的距离为0
    for(int i=0;i<n;i++)   //循环每个点,使得每个点都是最小距离点
    {
        int t=-1;  //标记未选中当前点
        for(int j=1;j<=n;j++)   //找到当前最小距离的点
        {
            if(!is_used[j]&&(t==-1||dis[j]<dis[t]))     t=j;
        }
        is_used[t]=true;   //把当前最小距离的点标记为最小距离的点
        for(int j=1;j<=n;j++)   //循环更新所有点的距离
            dis[j]=min(dis[j],dis[t]+g[t][j]);   
    }
    if(dis[n]==0x3f3f3f3f)  return -1;   //距离未更新,则不能到达
    else    return dis[n];
}
int main()
{
    memset(g,0x3f,sizeof g);
    memset(dis,0x3f,sizeof dis);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);   //取最小的重边
    }
    cout<<dijkstra()<<endl;
    return 0;
}

堆优化版实现:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int h[N],e[N],ne[N],w[N],idx;   //邻接表的构成
int n,m;
int dis[N];  //点到源的距离
bool is_used[N];   //判断点是否已经是最小距离点
typedef pair<int,int> PII;   //first存距离,根据距离进行堆排序;second表示点号
priority_queue<PII,vector<PII>,greater<PII>> heap;   //优先队列的定义
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;   //邻接表的连接方式
}
int dijkstraHeap()
{
    dis[1]=0;   
    heap.push({0,1});   //将1号点放入
    while(heap.size())
    {
        auto t=heap.top();   //去除最小距离的点,确定为当前最小距离点
        heap.pop();   //排出该点,去掉冗余
        int pos=t.second,distance=t.first;   
        if(!is_used[pos])   //如果这个点不是冗余,表示还不是最小距离点
        {
            for(int i=h[pos];i!=-1;i=ne[i])   //循环更新所有点的距离
            {
                if(dis[e[i]]>distance+w[i])  //源到直接该点的距离比经过当前最小点到该点的距离大
                {
                    dis[e[i]]=distance+w[i];   //更新距离
                    heap.push({dis[e[i]],e[i]});   //更新距离,找到下一个当前最小距离点
                }
            }
        }
        else continue;   //当前最小距离点已经是最小距离点,则直接进入下一层循环
    }
    if(dis[n]==0x3f3f3f3f)  return -1;
    else   return dis[n];
}
int main()
{
    memset(h,-1,sizeof h);
    memset(dis,0x3f,sizeof dis);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    cout<<dijkstraHeap()<<endl;
    system("pause");
    return 0;
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值