//顶点对最短路径迪杰斯特拉算法
#include<Windows.h>
#include<string.h>
#include<malloc.h>//malloc()//开辟空间
#include<limits.h>//INT_MAX,int型最大数
#include<stdio.h>//EOF(-1),NULL(空0)
#include<stdlib.h>//atoi();字符串转整形
#include<io.h>//eof()是否读到文件尾
#include<math.h>//floor()向下取整,ceil()向上取整,abs()绝对值
#include<process.h> //exit()退出
//函数结果代码
#define TRUE 1
#define FALSE 0
//正确1
#define OK 1
//错误-1
#define ERROR -1
//无穷大,设为-1
#define INFEASIBLE -1
//邻接矩阵和名称数量的最大值
#define MAXVEX 30
//最大名称字符数量
#define MAXNAME 20
//用来记录地点名称
typedef char VexType[MAXNAME];//顶点名类型(字符串类型)
//char原本是数组的类型,typedef后,char VexType[MAXNAME]是VexType的类型,Vextype只是一个名称
typedef int AdjType;//记录是否有边和边的长度
//定义一个图结构
typedef struct{
int n;//图中顶点个数
VexType vexs[MAXVEX];//顶点信息,记录各个顶点的名字 ,字符串类型,string==vexs
AdjType arcs[MAXVEX][MAXVEX];//邻接矩阵,储存边信息,记录顶点与顶点之间的距离
}GraphMatrix;
//想要传入原本数据,需要地址传递
//GraphMatrix图结构的别名
typedef struct{//保存最短路径的结构体
AdjType len;//从起点到当前节点的最短路径长度
int pre; //记录当前最短路径的前驱
}Path;
//遍历查找名为u的在图中的编号
int locateVex(GraphMatrix *g,VexType u)//图信息是地址传递
{
int i;
for(i=0;i<g->n;i++){//n个节点,依次遍历
if(strcmp(u,g->vexs[i])==0)//strcmp()等于0则意味着找到了该点的编号,编号就是在vexs数组中的位置,即为编号
return i;
}
return ERROR;//没找到就返回ERROR
}
//初始化图的信息
void Init(GraphMatrix *g) {
int i,j,k,w;
int edgeNums;//所有边的条数
VexType va,vb;//一条边的两个顶点
FILE *graphlist;//文件指针
graphlist=fopen("campusnav.txt","r");//打开数据文件,并以graphlist来接受文件的信息
fscanf(graphlist,"%d",&g->n);//读入所有顶点个数
fscanf(graphlist,"%d",&edgeNums);//读入所有边的个数
for(i=0;i<g->n;++i)
fscanf(graphlist,"%s",g->vexs[i]);//所有节点的名字
for(i=0;i<g->n;i++)//初始化邻接矩阵
for(j=0;j<g->n;i++){
g->arcs[i][j]=INT_MAX;//有权值的网,无路径则置路径为无穷,所以初始化邻接矩阵中每个值都是无穷大
}//limits.h头文件内的INT_MAX
//先都设为无穷大
for(k=0;k<edgeNums;k++){//依次将每个有路径距离的边赋值到初始化图中
fscanf(graphlist,"%s%s%d",va,vb,&w);//两点名称,两点之间距离 //读入字符串,不需要&,因为数组名本身就是数组的地址
i=LocateVex(g,va);//查两点编号,若没查到,则返回ERROR
j=LocateVex(g,vb);
if(i!=ERROR&&j!=ERROR){
g->arcs[i][j]=w;//找到了两点和知道两点间的边后,邻接矩阵内添加从va到vb的距离w
}else{//若是有一个点或是两个点读取错误,则报错
printf("%s<->%s顶点和边的读取错误,请您仔细检查!",va,vb);
exit(0);//process.h头文件中
//报错后,直接退出
}
}
for(i=0;i<g->n;i++){
g->arcs[i][i]=0; //各顶点自身到自身的权值为0
}
fclose(graphlist);//图的初始化结束,关闭读入的数据文件
}//到此,所有的点和边都以录入完毕
//绘制带权图的邻接矩阵
void printfGraphAdjacencyMatrix(GraphMatrix *g){
int i,j;
for(i=0;i<g->n;i++){
for(j=0;j<g->n;j++){
//遍历整个邻接矩阵
if(g->arcs[i][j]==INT_MAX){//若是邻接矩阵的某条边等于INT_MAX,即该条边不可通,则打印 ∞∞
printf(" ∞∞ ");
}else{
printf("%4d",g->arcs[i][j]);//若是该边有值,可通,则打印距离
}
}
printf("\n");
}
}
//迪杰斯特拉算法,计算两点之间的最短路径
void Dijkstra(GraphMatrix *pgraph,Path dist[],int start){
//GraphMatrix *pgraph是指向图的指针,即传入一个图结构;
//Path dist[]保存从起始点到下标所在该点最短路径信息的数组;
//int start 起始顶点编号
int i,j,min;
AdjType minw;//所在最小边长
dist[start].len=0;//将起始点到起始点的最短路径长度dist[start].len设置为0
dist[start].pre=0;//前驱顶点编号dist[start].pre也设置为0;
pgraph->arcs[start][start]=1;//表示顶点start以及已经加入集合U中,集合U(表示已经找到最短路径的顶点集合)即记录是否找到过该点,并且到自身的距离为1表示已经找过该点
for(i=0;i<pgraph->n;i++){//对集合V-U中的每个顶点i,初始化其到起始顶点start的距离为边的权值
dist[i].len=pgraph->arcs[start][i];//初始距离为起点到各个顶点的边的权值,有边的记录边的距离,无边的记录INT_MAX
if(dist[i].len!=INT_MAX)//若边存在则顶点i的当前前驱顶点设为start,不存在则置为-1,表示不存在
dist[i].pre=start;//存在从start直接到i的路径
else
dist[i].pre=-1;//不存在从start直接到i点,前驱为-1表示不存在
}
dist[start].pre=-1;//起点的前驱节点也置为-1;
//在集合V-U中选出距离值最小的顶点,将前驱置为已经加入集合U,同时调整集合V-U中其他顶点的最短路径
for(i=0;i<pgraph->n;i++){//循环遍历集合V-U中的每个顶点;
minw=INT_MAX;//最小权重minw初始化为最大整数INT_MAX,从大开始比较,最后得到当前最短边的值
min=start;//将当前顶点设置为起始顶点start
for(j=0;j<pgraph->n;j++){//遍历集合V-U中的每个顶点j
if((pgraph->arcs[j][j]==0)&&(dist[j].len<minw))//在V-U中选出距离值最小顶点,自身到自身的距离用来记录是否访问过
{
minw=dist[j].len;
min=j;//最后min为当前距离最短的点;
} //判断顶点j是否属于集合V-U(==0就是没加入),以及到起始顶点的距离是否小于当前最小权重
printf("%d %d\n",minw,min); }
if(min==0)//没有路可以通往任何顶点
break;
pgraph->arcs[min][min]=1;//路径最小的顶点设为min,置为已访问状态
for(j=0;j<pgraph->n;j++){//遍历集合V-U中的每个顶点j,调整其最短路径
if(pgraph->arcs[j][j]==1)//该顶点已经并入,已访问过,不用再考虑
continue;
printf("%d %d %d\n",dist[j].len,dist[min].len,pgraph->arcs[min][j]);
if(dist[j].len>dist[min].len+pgraph->arcs[min][j]&&dist[min].len+pgraph->arcs[min][j]>0)
//如果满足条件,使用当前的最小顶点来更新顶点j的最短路径信息
//dist[j].len>dist[min].len+pgraph->arcs[min][j]表示通过最小顶点min的路径会得到更短的最短路径。
//dist[min].len+pgraph->arcs[min][j]>0表示更新后的最短路径存在。
{
dist[j].len=dist[min].len+pgraph->arcs[min][j];//更新顶点j的最短路径
dist[j].pre=min;//更新前驱节点;
}
}
}
}
int main(int argc,char* argv[]){
system("color 3c");//控制台屏幕变蓝
GraphMatrix graph;//定义一个图
Path path[MAXVEX];//用于记录最短路径
int tmp,cnt=0,pre=-1;
int temppath[MAXVEX]; //去除最短路径所经过的节点
int m,n;
VexType va,vb;//待查询的两个地点
long totallen=0;//总路径长度
long curlen=0;//当前路径长度
Init(&graph);
printf("查找一下地点路径: \n");
int i;
for(i=0;i<graph.n;++i)//输出所有顶点名称
printf("%s",graph.vexs[i]);
printf("\n");
printf("\n邻接矩阵: \n");
printGraphAdjacencyMatrix(&graph);//输出邻接矩阵
printf("\n输入起点和终点\n");
scanf("%s%s",va,vb);//输入两点
m=LocateVex(&graph,va);//找到两点编号
n=LocateVex(&graph,vb);
if(m!=ERROR&&n!=ERROR){//两个顶点都在图中,则找出二者间最短路径可输出
Dijkstra(&graph,path,m);//迪杰斯特拉算法求最短路径
//因为求得的路径上顶点是从终点推到起点,现在将之逆置
for(tmp=0;tmp<MAXVEX;tmp++)
temppath[tmp]=-1;//temopath数组初始化为-1
pre=n;//最后位置到达的点位
while(path[pre].pre!=-1){//向前追溯最短路径经过的节点
temppath[cnt]=pre;
pre=path[pre].pre;
cnt++;
}
temppath[cnt]=m;//当检测到path[pre].pre!=-1时,退出,最后一个前驱没有记录,退出后再录入
if(cnt<=0)//cnt<=0表明没有经历cnt,即没有路径
if(m!=n)
printf("%s->%s这之间没有通路\n",graph.vexs[m],graph.vexs[n]);
else
printf("输入的起点与终点有重合!\n");
else
{
tmp=cnt;
printf("%s到",graph.vexs[temppath[tmp]]);
for(;tmp>0;tmp--){
printf("%s(%d)到",graph.vexs[temppath[tmp-1]],graph.arcs[temppath[tmp]][temppath[tmp-1]]);
totallen+=graph.arcs[temppath[tmp]][temppath[tmp-1]];
}
;
}
printf("距离共:%d米\n",totallen);
}
else
printf("(%s<->%s)输入的地点有不存在的地方,请您仔细检查!!",va,vb);
return 0;
迪杰斯特拉算法是,现以起始点更新每个点的最短路径,找每个没有访问并最短路径最短的点,更新最短点附近所有连接的点的最短路径,直到所有的点都标记为已访问为止,每次更新最短路径都要把他的前驱更新一下,如此便可追溯两点间的路径