Dijkstra

Dijkstra

  • 本质:BFS+贪心,对点进行操作

  • 特点:单源最短路,求解一个源点到其他所有点的最短距离

  • 存储结构:链式前向星

  • 适用对象:非负权图(一旦顶点已经确定最短路,其将不再被考查, d i s dis dis将不再改变);不能求最长路

  • 核心思想:不断贪心选取 {   d i s   } \set{dis} {dis}中最小的顶点 U U U,并更新其邻接点 V V V的最短路

  • 算法流程:设源点 s s s,已确定最终最短路点集 {   S   } \set{S} {S}(初始 {   S   } = ∅ ) \set{S}=\varnothing) {S}=);各点当前最短路长度 {   d i s   } \set{dis} {dis}(初始 {   d i s   } = + ∞ \set{dis}=+\infty {dis}=+(非 s s s,表示该点目前不可达), 0 ( s ) 0(s) 0(s)),目前最短路方案数组 p a t h path path(存储其上一个来源顶点,初始化为 − 1 -1 1表示无路径),待算法结束后即为最终最短路方案;当前轮起点 U U U U U U邻接点集 {   V   } \set{V} {V},邻接点权值集 {   W   } \set{W} {W}。三角形不等式: d i s ( s , v ) ( 最终 ) ≤ d i s ( s , u ) + d i s ( u , v ) dis(s,v)(最终)\le dis(s,u)+dis(u,v) dis(s,v)(最终)dis(s,u)+dis(u,v)
    循环执行下列步骤,直到点集 {   S   } \set{S} {S}中包含所有顶点或剩余未进入 {   S   } \set{S} {S}的顶点 d i s dis dis值均无法更新:

    1. 考查最小顶点(贪心):从点集 {   S   } \set{S} {S}中找 d i s dis dis最小且先前未被考查的顶点 U U U作为当前轮起点(首次即为源点 s s s),考查顶点 U U U

    2. 松弛入度边( B F S BFS BFS扩散):扩散到 U U U邻接点且先前未被考查的顶点 V V V,检查是否能通过 U U U作为中转而使 V V V d i s dis dis更短(松弛, d i s [ V ] = m i n ( d i s [ V ] , d i s [ U ] + W ) dis[V]=min(dis[V],dis[U]+W) dis[V]=min(dis[V],dis[U]+W),若被松弛, p a t h [ V ] = U path[V]=U path[V]=U)

  • 复杂度:朴素版 O ( V 2 ) O(V^2) O(V2),二叉堆优化版 O ( V log ⁡ 2 V + E ) O(V\log_2V+E) O(Vlog2V+E)

using ll=long long;
using pll=pair<ll,ll>;
const ll INF=__LONG_LONG_MAX__;
extern ll n,m,s;//点数,边数,源点
struct vertex{
    int e=-1;
}v[n];
struct edge{
    int u,v,w,n=-1;
}e[m];

朴素版

  • 选目前 d i s dis dis最小点 U U U的3个条件:该点未被考查,该点 d i s dis dis非无穷,该点 d i s dis dis更小
  • 扩散至 U U U邻接点 V V V的3个条件:该邻接点未被考查,边权值非无穷, V V V通过 U U U中转 d i s dis dis更短
void dijkstra(){
    vector<bool>S(n,0);
    vector<ll>dis(n,INF),path(n,-1);
    dis[s]=0;
    for(ll k=0;k<n;k++){//尽可能使未选点集添加到已选点集
        ll U=-1,minn=INF;
        for(ll i=0;i<n;i++)//贪心选dis最小点
            if(!S[i]&&dis[i]!=INF&&dis[i]<minn)//3个条件:该点未被考查,该点dis非无穷,该点dis更小
                U=i,minn=dis[i];
        if(U==-1) break;//特判:剩余点均不可达
        S[U]=1;//考查最小点
        for(ll i=v[U].e;~i;i=e[i].n){//BFS扩散邻接点 ~i:i!=-1
            ll V=e[i].v,W=e[i].w;
            if(!S[U]&&W!=INF&&dis[V]>dis[U]+W)//松弛3个条件:该邻接点未被考查,边权值非无穷,邻接点通过当前起点中转dis更短
                dis[V]=dis[U]+W,path[V]=U;
        }
    }
}

二叉堆优化版

  • 选目前 d i s dis dis最小点 U U U的3个条件:该点未被考查,该点 d i s dis dis非无穷,该点 d i s dis dis更小。选取 d i s dis dis最小点是通过另设 d i s dis dis的小根堆 Q Q Q动态维护目前 d i s dis dis最小点 U U U实现的。

  • 扩散至 U U U邻接点 V V V的3个条件:该邻接点未被考查,边权值非无穷, V V V通过 U U U中转 d i s dis dis更短

void dijkstra(){
    vector<bool>S(n,0);
	vector<ll>dis(n,INF),path(n,-1);
    priority_queue<pii,vector<pii>,greater<>>pq;//小根堆,first存顶点dis,second存顶点编号
    dis[s]=0,pq.push({dis[s],s});
    while(pq.size()){
        ll U=pq.top().second;
        pq.pop();
        if(S[U]) continue;//若已被考查过,直接跳过
        S[U]=1;//考查堆顶最小点
        for(ll i=v[U].e;~i;i=e[i].n){//BFS扩散邻接点 ~i:i!=-1
            ll V=e[i].v,W=e[i].w;
            if(!S[V]&&W!=INF&&dis[V]>dis[U]+W)//松弛邻接点入度边
                //松弛3个条件:该邻接点未被考查,边权值非无穷,邻接点通过当前起点中转dis更短
                dis[V]=dis[U]+W,path[V]=U,pq.push({dis[V],V});
        }
    }
}

路径输出

void print(ll s,ll t){
    if(!(~path[t])){cout<<-1<<' ';return;}
    if(s==t){cout<<s<<' ';return;}
    print(s,path[t]);
    cout<<t<<' ';
}

正确性证明

posted @   椰萝Yerosius  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示