Dijkstra算法
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
图片以及案例来自简书的这篇博客https://www.jianshu.com/p/ff6db00ad866
#include<stdio.h>
#include<iostream>
#include<math.h>
#define INF 1e5+10 //此值当作无穷大
const int size=5; //地点个数,即地图尺寸大小
/*
1.指定一个节点,例如我们要计算 'A' 到其他节点的最短路径
2.引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞)
3.初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0,
U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞,敲黑板!!!接下来要进行核心两步骤了
4.从U集合中找出路径最短的点,加入S集合,例如 A->D = 2
5.更新U集合路径,if ( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' ) 则更新U
6.循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径
*/
typedef struct Graph_data //地图数据结构体类型
{
int mat[size][size];
int fmat[size][size];//用于判断走最短路径时是否会结束此段i-j路径
int vnum; //点数
int edgenum; //边数
char vname[size];
} graph;
typedef struct Process_data
{
int process[size]; //该数组用来存储行走路径的过程
char name;
int dist;
int prev; //记录i点的前一点是谁
} ps; //可以理解成每个点的状态
void dijkstra(graph G,ps v[],int vs)
{
//初始化
int ok=1;
int flag[size]={0};
int fmat[size][size]={0};
memcpy(G.fmat,fmat,sizeof(fmat));
flag[vs]=1;
int process[size]={0};
for(int i=0;i<G.vnum;i++)
{
memcpy(v[i].process,process,sizeof(process));//初始化过程数组
v[i].dist=G.mat[vs][i]; //i点到起点的距离
v[i].name=G.vname[i];
v[i].prev=vs;
}
//遍历整张图,找到每个点的最短路径及其长度
int k=vs;
while(ok)
{
ok=0;
int min=INF,tmp;
for(int i=0;i<G.vnum;i++)
{
if(flag[i]==0&&v[i].dist<min) //如果到i的距离不是最短的且当前i的距离小于min则该路径是当前最短的,就要选择该点i
{
k=i;
min=v[i].dist;
}
}
flag[k]=1;//遍历更新为已遍历,即更新集合S
//注意理解遍历更新,尤其理解C点的最短路径和E点的最段路径的过程
//以下循环作用是更新集合U
for(int i=0;i<G.vnum;i++)
{
tmp=(G.mat[k][i]==INF?INF:(min+G.mat[k][i]));
if(flag[i]==0&&tmp<v[i].dist)
{
v[i].prev=k;
v[i].dist=tmp;
G.fmat[k][i]=1;
}
}
//判断是否都已遍历
for(int i=0;i<G.vnum;i++)
if(flag[i]==0) ok=1;
}
//将过程输入结构体的process数组中
for(int i=0;i<G.vnum;i++)
{
int tp=i,j=size-1;
while(tp!=vs)
{
v[i].process[j--]=tp;
tp=v[tp].prev;
}
}
}
int main()
{
graph G;
int vs=0;//起点
int mat[5][5]={
{0 ,4 ,INF,2 ,INF},
{4 ,0 ,4 ,1 ,INF},
{INF,4 ,0 ,1 ,3 },
{2 ,1 ,1 ,0 ,7 },
{INF,INF,3 ,7 ,0 }
};//输入无向图
memcpy(G.mat,mat,sizeof(mat));
G.vnum=size;
G.edgenum=7;
char vname[5]={'A','B','C','D','E'};
memcpy(G.vname,vname,sizeof(vname));
ps v[size];
dijkstra(G,v,vs);
for(int i=1;i<G.vnum;i++)
{
printf("The distance of %c is %d\t",G.vname[i],v[i].dist);
printf("%c-",G.vname[vs]);
for(int j=0;j<G.vnum;j++)
{
if(v[i].process[j]!=vs)
{
printf("%c%c",G.vname[v[i].process[j]],(v[i].process[j]==i?'\0':'-'));
}
}
printf("\n");
}
return 0;
}
运行结果:
typedef与#define的比较
以上代码中涉及到一些typedef的用法,此博客对这个知识点有很透彻的介绍:
http://blog.sina.com.cn/s/blog_4826f7970100074k.html
在此博客讲到了#define来替换数据类型名称时只是简单的替换了对应字符串,在这一功能的实现上typedef要比#define更靠谱,我们来通过以下列子来比较:
#define PINT int*
typedef int* pint
//a,b都为int*类型
pint a,b;
//预处理时替换为int* c,d;则c为int* 类型,而d为int类型
PINT c,d;