Dijkstra算法
单源最短路问题
可以求出指定某个点到其他所有点的最短距离
构建
邻接矩阵的构造
没有相连接的两个结点的距离时无穷大,本身到本身为0,其他正常
1.先把所有的结点的距离初始化为无穷大
2.本身与本身的距离设为0
3.输入距离
距离数组的构造
构造一个一维距离数组来记录该点到其他点的最短距离
1.进行赋值,从邻接矩阵中赋值
2.本身到本身设为0
复杂度分析
O((o^{2}))
优化后O((nlog_{n}))
思路
对每一个点进行遍历
找个该点的相连点中,距离该点最近的点,然后标记最近点
从标记的最近点进行遍历最近点相连接的点,进行距离更新
因为距离用的是指定某个点的距离,所以最后求出来的距离数组是对于该点来说的
相当于求出某个点a到另个点b的最短距离,然后这个距离和指定点到b的距离进行比大小,求距离短的那条路
比如
遍历为1时,表示结点为1的点进行查找,找到1的最近点,
然后对该点的连接点进行查找,比较指定点到这个点的距离和指定点先到1再到这个点的距离哪个近,近的变成指定点到该点的距离
最短路问题模板
输出最短距离
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define INF 1000000
using namespace std;
const int maxn=1005;
int edges[maxn][maxn];//邻接矩阵表示
int n;//点的数
int m;//边数
int dis[maxn];//储存指定点到其他点的距离
int vis[maxn];//标记顶点是否被访问过
void init(){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i==j)edges[i][j]=0;//到本身的距离
else edges[i][j]=INF;//无穷大
}
vis[i]=0;
}
int a,b,d;
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&d);
if(edges[a][b]>d){
edges[a][b]=edges[b][a]=d;
}
}
}
void Dij(int s){
for(int i=1;i<=n;i++){
dis[i]=edges[s][i];
}
vis[s]=1;//初始点标记
int minn,mark;//进行查找,找到与当前点相连接的且距离最短的一个点,进行标记
for(int i=1;i<=n;i++){
minn=INF;
for(int j=1;j<=n;j++){
if(vis[j]==0&&dis[j]<minn){
minn=dis[j];
mark=j;
}
}
vis[mark]=1;//标记该点
//对这个标记点的相连点进行距离更新
for(int k=1;k<=n;k++){//遍历所有点
if(edges[mark][k]<INF){//如果说当前的点与前面标记的点相互连接
if(vis[k]==0&&dis[k]>dis[mark]+edges[mark][k]){//进行判断,看看是否可以更新为更小的的距离
//dis[k]是指定点到该点的距离,dis[mark]是指定点标记点的距离,edges[mark][k]是标记点到该点的距离
dis[k]=dis[mark]+edges[mark][k];
}
}
}
}
}
int main(){
while(scanf("%d%d",&n,&m)==2){
if(n==0&&m==0)break;
init();//初始化并且创建;邻接矩阵
int s,t;
scanf("%d%d",&s,&t);
Dij(s);
if(dis[t]==INF){
printf("-1
");
}else{
printf("%d
",dis[t]);
}
}
return 0;
}
输出路径
加个path[]一维数组,且在dij算法里两处不断更新
然后用while语句访问,while(path[]!=-1)不断变化下标,但是这样只能倒着输出
可以再创建一个数组或者是栈,使得正序输出
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define INF 1000000
using namespace std;
const int maxn=1005;
int edges[maxn][maxn];//邻接矩阵表示
int n;//点的数
int m;//边数
int dis[maxn];//储存指定点到其他点的距离
int vis[maxn];//标记顶点是否被访问过
int path[maxn];
void init(){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i==j)edges[i][j]=0;//到本身的距离
else edges[i][j]=INF;//无穷大
}
vis[i]=0;
}
int a,b,d;
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&d);
if(edges[a][b]>d){
edges[a][b]=edges[b][a]=d;
}
}
}
void Dij(int s){
for(int i=1;i<=n;i++){
dis[i]=edges[s][i];
path[i]=-1;
}
vis[s]=1;//初始点标记
int minn,mark;
for(int i=1;i<=n;i++){
minn=INF;
for(int j=1;j<=n;j++){
if(vis[j]==0&&dis[j]<minn){
minn=dis[j];
mark=j;
path[mark]=i;//不断更新
}
}
vis[mark]=1;//标记该点
for(int k=1;k<=n;k++){
if(edges[mark][k]<INF){
if(vis[k]==0&&dis[k]>dis[mark]+edges[mark][k]){
dis[k]=dis[mark]+edges[mark][k];
path[k]=mark;//不断地更新mark后面地那个点
}
}
}
}
}
int main(){
while(scanf("%d%d",&n,&m)==2&&n&&m){
init();
int s,t;
scanf("%d%d",&s,&t);
Dij(s);
for(int i=1;i<=n;i++)printf("%d ", path[i]);//-1表示不走,path[]的值是点的前一个位置的下标
}
return 0;
}
双重权值
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1005;
int edges[maxn][maxn],edgec[maxn][maxn];//邻接矩阵表示
int n,m;//边和点
int dis[maxn],cost[maxn];//储存指定点到其他点的距离
bool vis[maxn];//标记顶点是否被访问过
void init(){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i==j)edges[i][j]=edgec[i][j]=0;//到本身的距离
else edges[i][j]=edgec[i][j]=INF;//无穷大
}
vis[i]=false;
}
}
void Dij(int s){
for(int i=1;i<=n;i++){
dis[i]=edges[s][i];
cost[i]=edgec[s][i];
}
int minn,mark;
for(int i=1;i<=n;i++){
minn=INF;
for(int j=1;j<=n;j++){
if(vis[j]==0&&dis[j]<minn){
minn=dis[j];
mark=j;
}
}
vis[mark]=true;
for(int k=1;k<=n;k++){
if(edges[mark][k]<INF){
if(!vis[k]&&dis[k]>dis[mark]+edges[mark][k]){
dis[k]=dis[mark]+edges[mark][k];
cost[k]=cost[mark]+edgec[mark][k];
}else if(!vis[k]&&dis[k]==dis[mark]+edges[mark][k]){
if(cost[k]>cost[mark]+edgec[mark][k]){
cost[k]=cost[mark]+edgec[mark][k];
}
}
}
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(n==m&&n==0)break;
init();//初始化并且创建;邻接矩阵
int a,b,d,p;
for(int i=0;i<m;i++){
scanf("%d%d%d%d",&a,&b,&d,&p);
if(edges[a][b]>d){
edges[a][b]=edges[b][a]=d;
edgec[a][b]=edgec[b][a]=p;
}
}
int s,t;
scanf("%d%d",&s,&t);
Dij(s);
printf("%d %d
",dis[t],cost[t]);
}
return 0;
}
优化
- 优先队列
- 前向星
- 邻接表代替邻接矩阵
链式前向星+优先队列优化(边少的情况)
/*
输出s到t的最短路
输入n,m
以及m个u,v,w
输出最短路
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5,maxm=2e5+5;
int head[maxm],vis[maxn],dis[maxn];//dis[]数组表示到制定点的最短距离,vis[]数组表示某个点是否走过
int n,m,s,t;
int cnt=1;//记录图的结构体的下标
int pre[maxn];//存路径用
struct edge{
int to;//到哪
int from;//从哪开始
int w;
}e[maxm];
inline void add(int u,int v,int w){//链式前向星存图
e[cnt].to=v;
e[cnt].w=w;
e[cnt].from=head[u];
head[u]=cnt;
cnt++;
}
struct node{
int dis;//dis表示到起始点的距离
int pos;//pos表示该点的下标
bool operator < (const node &x)const{//符号重载,对距离排序,针对于在优先队列里
return x.dis<dis;
}
};
std::priority_queue<node> q;//构造优先队列
void dijkstra(){
q.push((node){dis[s],s});//把初始位置和距离放进优先队列
while(!q.empty()){
node tmp=q.top();
q.pop();
int u=tmp.pos;
if(vis[u])continue;//如果这个点走过了
vis[u]=1;//标记走过
for(int i=head[u];i;i=e[i].from){
int v=e[i].to;
if(!vis[v]&&dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
pre[v]=u;
q.push((node){dis[v],v});
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
s=1,t=n;//s到t
for(int i=1;i<=n;i++)dis[i]=INF;//初始化
dis[s]=0;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);//构成无向图
}
dijkstra();
printf("%d
",dis[t]);//输出1到n的最短路
}
邻接表+优先队列(点少的情况)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
struct edge{
int to,cost;
};
int vis[maxn];//表示该点是否走过
int n,m,s,t;//n个点,m个边,s起点,t终点
int dis[maxn];//最短距离
struct HeadNode{
int d,u;//d为距离,u为结点位置
bool operator < (const HeadNode& rhs) const{//重载HeadNode结构体的<运算符,如果放在优先队列里,将会按照距离排序
return d>rhs.d;
}
};
vector<edge> g[maxn];
void Dij(){
for(int i=1;i<=n;i++){
dis[i]=INF;
}
dis[s]=0;
priority_queue<HeadNode>q;
q.push((HeadNode){0,s});//把类型为HeadNode,值为0,s
while(!q.empty()){
HeadNode x = q.top();//x是队列顶
q.pop();//把顶取出,顶就是距离最短的那个点
int u=x.u;
if(vis[u])continue;
vis[u]=1;
for(int i=0;i<g[u].size();i++){//遍历与距离最短点相连接的点
edge e=g[u][i];
if(dis[e.to]>dis[u]+e.cost){//如果直接到该点的距离大于从通过u点再到该点的距离的话
dis[e.to]=dis[u]+e.cost;//松弛
q.push((HeadNode){dis[e.to],e.to});
}
}
}
}
int main(){
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
edge e;
int x;
scanf("%d%d%d",&x,&e.to,&e.cost);//x出发到e.to,花费e.cost
g[x].push_back(e);//只有这个的话就是有向图
g[e.to].push_back((edge){x,e.cost});//构建无向图
}
Dij();
if(dis[t]==INF)printf("-1
");//不存在最短路
else printf("%d
",dis[t]);
return 0;
}
输出路径模板
增加pre[数组],pre[s]=0,反向输出路径
#include<bits/stdc++.h>
using namespace std;
const int inf=1000000002;
const int maxn=100100,maxm=200200;
int head[maxm],vis[maxn],dis[maxn];//dis[]数组表示到制定点的最短距离,vis[]数组表示某个点是否走过
int i,j;
int u,v,w,z;
int n,m,s,t;
int cnt=1;//记录图的结构体的下标
int pre[maxn];
struct edge{
int to;//到哪
int from;//从哪开始
int w;
}e[maxm];
inline void add(int u,int v,int w){
e[cnt].to=v;
e[cnt].w=w;
e[cnt].from=head[u];
head[u]=cnt;
cnt++;
}
struct node
{
int dis;//dis表示到起始点的距离
int pos;//pos表示该点的下标
bool operator < (const node &x)const{//符号重载,对距离排序,针对于在优先队列里
return x.dis<dis;
}
};
std::priority_queue<node> q;//构造优先队列
inline void dijkstra()
{
q.push((node){dis[s],s});//把初始位置和距离放进优先队列
while(!q.empty()){
node tmp=q.top();
q.pop();
int u=tmp.pos;
if(vis[u])continue;//如果这个点走过了
vis[u]=1;//标记走过
for(i=head[u];i;i=e[i].from){
int v=e[i].to;
if(!vis[v]&&dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
pre[v]=u;
q.push((node){dis[v],v});
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
s=1;//起始点
pre[s]=0;
for(i=1;i<=n;i++)dis[i]=inf;//初始化
dis[s]=0;
for(i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dijkstra();
printf("%d
",dis[n]);
while(pre[n]!=0){
printf("%d
",pre[n]);
n=pre[n];
}
}