最短路dijkstra朴素及其优化

 

dijkstra算法:

图的存储:

稀疏图可以用邻接表来存

稠密图可以用邻接矩阵来存

这个算法基于稠密图,所以应该用邻接矩阵来存

算法思想:

创建一个集合s表示所有确定下来最短距离的点

用t来表示没有确定下来最短距离的点

用完t之后,把t放入s

然后一次次的迭代更新,保证每一个点到起点的距离都是最短距离

 

 举例:

 

 此时到起点距离最短的点就是dist[1]这个点,距离为0

然后开始迭代检查更新:

从1号点可以到达2号和3号,2号到起点的最短距离为dist[1]+2等于2小于正无穷,可以被更新为2

3号到起点的距离为dist[1]+4等于4等于4小于正无穷,3号点可以被更新为4

但此时只是迭代到1号点,刚刚的2,3号点只是被更新而已并没有确定下来,因此更新完之后,s集合只有1号点,2,3继续待定

 

 第二次迭代:再次没有确定的点中找到最小值

此时的2号点(没有确定)到起点是最短的,可以把2号点放入集合s中了

再接着,用2号点来更新它到其他点与起点的最短路径:

 

 2号点只可以到3号点

3号此时的距离是4,2号到起点的距离是2,从2号到3号:3号到起点的最短距离:2号到起点的最短距离+1也就是dist[2]+1等于3小于4,所以此时需要更新

 

 再下一轮迭代:

就剩一个3号点了,那此时的3号点就是之前更新过的3,直接加入s集合

时间复杂度O(n*n)

对于代码的解释我下面给出注释了:

复制代码
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
const int N=510;
int g[N][N],dist[N];
bool st[N];
int dijkstra(){
    memset(dist,0x3f,sizeof(dist));//除了第一个,其他的都初始化为正无穷好方便取值
    dist[1]=0;
    for(int i=0;i<n;i++)//n次迭代,其实可以到n-1次,最后一次可以省略 
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if((!st[j])&&(t==-1||dist[j]<dist[t]))//t==-1设置是为了能找到第一个没被标记的点
            //用这个点作为基准然后与后面的进行比较,找出全部未被标记且路径最短的点
                t=j;//更新1到n中路径最小的那个点且未被标记
        st[t]=true;
        for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+g[t][j]);//更新                                  
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    else dist[n];
    
}
int main(){
    memset(g,0x3f,sizeof(g));//初始化无穷大,以便于下面重边取最小值的比较 
    cin>>n>>m;
    while(m--)
    {
        int a,b,z;
        cin>>a>>b>>z;
        g[a][b]=min(z,g[a][b]);//可能会有重边,因为求的是最短路径,只要可以连通,取最小值就可以了,其他的可以省略 
    }
    int t=dijkstra();
    cout<<t<<endl;
    return 0; 
}
/*
先初始化
接着求出未在s中的最短的
放入s
更新 
主要就是这个四步 

*/ 
复制代码

 以上算法的复杂度是O(n*n),下面介绍堆排序优化的dijkstra算法:

这个复杂度只需要O(mlogn)

其实算法的思想还是基于上面的,但用到了堆的数据结构

堆的可以执行以下操作:找出最小的,修改。删除

也可以用手写堆和STL里面的优先队列来维护这个堆

下面的主要注释在代码里面,思想与上面差不多哦

复制代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> PAII;
int n,m;
const int N=150000;//在边是点的次方时是稠密图用邻接矩阵来存,二者差不多用邻接表来存,这里是稀疏图选择邻接表 
int h[N],e[N],ne[N],idx,w[N];//w代表权重 
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
int dijkstra(){
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    priority_queue<PAII,vector<PAII>,greater<PAII>> heap;//小根堆,第一个为最小的 
    heap.push({0,1});//堆里面存的是点,一定是第一个为距离,第二个为点的序号,pair排序默认了先按第一个进行排序,那肯定是按照距离排序 
    //下面是找到未被标记的距离最短的点
    while(heap.size())
    {
        auto t=heap.top();
        heap.pop();
        int dis=t.first,ver=t.second;
        if(st[ver]) continue;//如果此时的点被标记过则跳过
        else st[ver]=true;//否则就使用这个点,因为堆里面的数据都是经过排序过的,所以只要遇到没被标记过的,那就直接是距离最短的那个点
        for(int i=h[ver];i!=-1;i=ne[i])//更新 
        {
            int j=e[i];//所对应点的坐标 
            if(dist[ver]+w[i]<dist[j])//dist表示这个点到起点的距离
            {
                dist[j]=dist[ver]+w[i];
                heap.push({dist[j],j});//把新更新过的点但没有标记哦放入堆 
            } 
        }      
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    else return dist[n]; 
} 
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof(h));//把邻接表初始化 
    while(m--)
    {
        int a,b,z;
        cin>>a>>b>>z;
        add(a,b,z);//这里可以不用像邻接矩阵那样考虑重边,可以被更新取最小值,只是堆里面会有很多冗余 
    }
    int t=dijkstra();
    cout<<t<<endl;
    return 0;
}
/*
其实堆的作用就是代替朴素版的找未被标记的距离最近的点 ,其他的不变
用不着想的多高大上... 

*/
复制代码

 

posted @   小志61314  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示