dijkstra
/*将顶点分为两个集合:已求得最短距离的集合1,待求集合2。
(1) 在集合2中找一个到start距离最近的顶点k : min{d[k]}
(2) 把顶点k加到集合1中,同时检查集合2中的剩余顶点j的d[j]是否
经过k后变短,如果变短修改d[j]。
if ( d[k]+a[k][j]<d[j] ) d[j]=d[k]+a[k][j]
(3) 重复(1)和(2),直至集合2空为止。*/
//不能处理负边,也不能处理负权回路
#include<iostream>
#include<string.h>
#define TNF 2147483647
using namespace std;
long long int dis[500005];//dis表示两个点之间的距离
long long int book[500005];//book表示该点是否被打了标记
long long int value[500005],to[500005];//value为各个边的长度;to为各边连接的点
long long int head[500005] ;
long long int next[500005];//next函数表示该条直线的初始点连接的下一条点
long long int m,n,s,total;//totle为总的个数;//m 为点的个数;n为边的个数;s为出发点
void add(long int a,long int b,long int c)//a为起点,b为终点,c为长度/*目的是插入这个点和连接的点以及两点之间的长度
{
total++;
to[total]=b;
value[total]=c;
next[total]=head[a];
head[a]=total;
}
void dijkstra(int u)//计算长度 u为出发点;
{
for(int i=1;i<=n;i++)
{
dis[i]=TNF;
}//初始化,附一个极大的值
memset(book,0,sizeof(book));//给标记初始化,所有的点都被标记为0;后来打标记为1加以区分
dis[u]=0;//按题目中所说改电到出发点的距离都是本身即为0
for(int i=1;i<=n;i++)//全都扫一遍
{
int start=-1;//点的标记是从0开始,不存在-1的点
for(int j=1;j<=n;j++)
{
if(book[j]==0&&(dis[j]<dis[start]||start==-1))start=j;
}
//这样不断的for一定可以找出一个距离已经进队的元素最近的一个
book[start]=1;//start这个元素进队,然后更新以下没有进队的元素与已经进队的元素的最近距离,如果可以更新的话
for(int e=head[start];e;e=next[e])//更新
{
dis[to[e]]=min(dis[to[e]],dis[start]+value[e]);
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
dijkstra(s);
for(int i=1;i<=n;i++)
{
cout<<dis[i]<<" ";
}
}
Floyd
//可以处理负边,但是无法处理负权回路
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define maxn 0x7f7f
using namespace std;
int n,m,s;
int dis[10000][10002];
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((dis[i][k]+dis[k][j])<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)
{
dis[i][j]=0;
}
else{
dis[i][j]=maxn;
}
}
}
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
dis[x][y]=z;
dis[y][x]=z;
}
floyd();
cout<<dis[1][n]<<" ";
}
/*
floyd算法可以求任意两点之间的最短路
*/
spfa +矩阵
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define INF 9999999
using namespace std;
int a[1000][1000];//用邻接矩阵来存储图
int q[10001],dis[10001],b[201][201];
bool vis[201];
int m,n,s,t;
void spfa(int s)
{
for(int i=1;i<=n;i++)
{
dis[i]=INF;
}
dis[s]=0;vis[s]=1;q[1]=s;
int head=0,tail=1;
while(head<=tail)//队列不为空就可以一直进行操作
{
head++;//this line &next line 取出队首的操作
int v=q[head];//队首
vis[v]=0; //取出队首以后一定要释放标记,保证可以重复进队
for(int i=1;i<=b[v][0];i++)
//取出当前节点要用当前的节点去更新和他相连的所有节点
// 如果有节点没有进队,加入队列中
{
if(dis[b[v][i]]>dis[v]+a[v][b[v][i]])
{
dis[b[v][i]]=dis[v]+a[v][b[v][i]];
if(vis[b[v][i]]==0)
{
tail++;
q[tail]=b[v][i];
vis[b[v][i]]=1;
}
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
if(a[x][y]!=0&&z>a[x][y])//若x,y之间不只有一条,保留最小的
continue;
//对于next line的解释,以x为节点的变数++,并且以x为其中的 一个点
// ,第 b[x][0]条边所连接的另一个点是y,并且这两个点之间的权值为z
b[x][0]++;b[x][b[x][0]]=y;a[x][y]=z;
//b[x][0],以x为一个节点的边的条数
b[y][0]++,b[y][b[y][0]]=x;a[y][x]=z;//双向建边
}
cin>>s>>t;//输入起点和终点
spfa(s);
if(dis[t]!=INF)cout<<dis[t]<<endl;
else{
cout<<-1<<endl;
}
return 0;
}
各种最短路算法比较 1.dijkstra : 不能处理负边,不能处理负权回路,侧重对点的处理,适用于稠密图(和顶点之类的进行联系)时间复杂度,(m+nlogn)空间复杂度,m 2.floyd :侧重于对点的处理,可以处理负权边,但是不能处理负权回路,适用于稠密图,空间复杂度n^2,时间复杂度,n^3,只有数据规模较小且时空复杂度都允许时才可以使用 3.bellman-ford :可以处理负权边,但是不能处理负权回路,适用于稀疏图,时间复杂度m,空间复杂度(mn),侧重于对边的处理 4.spfa实际上就是队列优化的bellman-ford,空间复杂度M,时间复杂度NM,侧重于对边的处理 适用于稀疏图,可以解决负权边和负权回路