定义:表示顶点u到顶点v的一条边的权值(边权)
最短路径算法有常见的四种:floyd,dijkstra,Bellman-Ford,SPFA
不过Bellman-Ford并不常用,所以本文不提;
重点在于dijkstra,spfa;
floyd(O(n^3))
思想是对所有边进行松弛操作。
code:
for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){f[i][j]=min(f[i][j],f[i][k]+f[k][j]);//松弛操作 }}
}
代码解读:
可以把k理解为的中转点,而后
故:描述不太严谨
形象一些:
上图即为所有最短路径算法的普遍思想。
dijkstra(O(n^2+m))
把节点分为两个集合,一是以确定的出现在最短路径上的点集合,记为S;二是没确定的节点记为集合T,一开始所有点都属于T集合。其实就是著名的蓝白点思想可以去百度搜一搜。
code:
//无向图邻接矩阵存图;
for(int i=1;i<=n;i++){dis[i]=f[s][i];//dis[i]=E(s,i)
}
for(int i=1;i<=n-1;i++){//枚举中转点 k=0;minl=0x3ffffff;//初始为无穷 for(int j=1;j<=n;j++){if(!vis[j]&&dis[j]<minl){minl=dis[j];k=j;}}if(k==0)break;//没找到中转点直接退出 vis[k]=1;//标为白点 for(int j=1;j<=n;j++){dis[j]=min(dis[j],dis[k]+f[k][j]);//松弛操作 }
}
return dis[e];//min(E(s,e))
SPFA(O(KE) or O(nm))
关于复杂度:其中k为所有顶点进队的平均次数,可以证明k一般小于等于2;
SPFA就是Bellman-Ford的一种实现方式,使用了队列优化让复杂度变低
spfa也会用到松弛操作。
前置芝士:链式前向星
//用链式前向星来存图
void add(int a,int b,int c){//从a到b的一条边权为c的边 to[++cnt]=b;val[cnt]=c;nxt[cnt]=h[a];h[a]=cnt;
}
code:
queue<int>q;//队列
void spfa(){for(int i=1;i<=n;i++){dis[i]=0x3fffff;//初始为无穷 }dis[s]=0;vis[s]=1;q.push(s);//入队(把源点加入队列) while(!q.empty()){int u=q.front();q.pop;//弹出队列 vis[u]=0;//没被加入队列过 for(int i=h[u];~i;i=nxt[i]){//找h[u]的所有连边进行松弛操作 if(dis[u]+val[i]<dis[to[i]]){dis[to[i]]=dis[u]+val[i];//对该边进行松弛操作 if(!vis[to[i]]){//若没有加入队列 q.push(to[i]);//加入队列 vis[to[i]]=1;//标记为已经加入队列(白点) }}}}
}
在随机数据下spfa直接封神。
但如果不是随机数据,且有一堆特殊情况而且数据毒瘤,要设置分层图时,不要用spfa