最短路径问题:
从图的一个顶点出发,到达图的另一个顶点的最短路径
解法:
迪杰斯特拉算法(Dijkstra算法)
弗洛伊德算法(Floyd算法)
SPFA算法
Dijkstra算法介绍
主要是通过广度优先遍历,对每个点进行松弛操作找到源点到其他每个点的最短路径
具体实现流程:
一、初始化图
1、首先定义图的结构体包含邻接数组和顶点数组
2、然后输入每一个顶点的值,然后按输入顶点顺序来输入邻接数组的每个值
3、为了方便自己到自己可以设置为0,没有路径设置为-1,有路径设置路径值
4、对邻接数组进行加工,把距离为-1的设置为默认的无穷大
二、通过Dijkstra求最短路径
思想:通过一个距离数组不断松弛求最短路径,松弛过程中记录通过的点
第一次将源点的数据记录到距离数组并将源点标记,然后依次找最近的未标记的点进行松弛操作,所有的点标记后得到的数组就是到其他每个点的最短距离,可以通过前驱节点来找到对应的路径。
松弛操作:比如1 2 3 4四个顶点求1到4的最短路径,首先加入1,通过邻接矩阵得到1到其他每个点的最短路径,然后加入离1最近的点(比如3),松弛 其他距离(判断1到2的直接距离小还是通过3到2 的间接距离小)(判断1到4的直接距离小还是通过3到4的间接距离小),依次对其他每个未标记的点进行判断取最小
5、首先定义需要的数组,包含used判断每个顶点是否已经被标记
6、通过距离数组记录源点到其他点的距离
7、前驱节点数组用来记录当前节点离源点最近的路径的上一个点,用来寻找最短路径
8、初始化数组,标记数组都设置为未标记,源点设置为已标记,距离数组读取邻接矩阵中源点的行,前驱节点都设置为自己
9、从源点开始找最近的点一次进行松弛操作,找到最近且未标记的点,判断到其他未标记的点是不是可以通过这个新加入的点所短路径,如果可以缩短就修改距离和前驱阶段,直到所有顶点都被标记
10、通过前驱节点数组找到对应路径,通过距离数组得到最短值。
实现代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MaxSize 20 #define INFINITY 666 typedef char VertexType; //定义图 的邻接矩阵表示法结构 typedef struct Graph { VertexType ver[MaxSize+1];// 顶点表,是一位数组 int edg[MaxSize][MaxSize];// 邻接矩阵,可以看做是边表 }Graph; //邻接矩阵法图的生成函数 void CreateGraph( Graph *g ){ int i = 0; int j = 0; int VertexNum; VertexType Ver; while( ‘\n‘ != (Ver=getchar()) ) g->ver[i++] = Ver; g->ver[i] = ‘\0‘; VertexNum = strlen(g->ver); for( i=0; i<VertexNum; i++ ) for( j=0; j<VertexNum; j++ ) scanf("%d", &g->edg[i][j]); } //求图的顶点数 int CalVerNum( Graph g ){ return strlen(g.ver); } //将不邻接的顶点之间的权值设置为INFINITY void SetWeight( Graph *g ){ int i, j; for( i=0; i<CalVerNum(*g); i++ ){ for( j=0; j<CalVerNum(*g); j++ ){ if( -1 == g->edg[i][j] ) g->edg[i][j] = INFINITY; } } } //Dijkstra求最短路径函数 void Dijkstra( Graph g ){ int VertexNum = CalVerNum( g ); int i, j, k, m, n; int mini; int *used = (int *)malloc(sizeof(int)*VertexNum); int *distance = (int *)malloc(sizeof(int)*VertexNum); int *parent = (int *)malloc(sizeof(int)*VertexNum); SetWeight( &g ); //设置权值 printf("读取到的顶点依次为:"); for( i=0; i<VertexNum; i++ ){ printf(" %c ",g.ver[i]); } printf("\n\n"); printf("读取到的邻接矩阵为(666表示无穷大)\n"); for( i=0; i<VertexNum; i++ ){ for( j=0; j<VertexNum; j++ ){ printf("%d ",g.edg[i][j]); } printf("\n"); } printf("\n"); printf("请输入起点和终点(空格隔开):"); char a,b; int st,en; fflush(stdin); scanf("%c %c",&a,&b); for( i=0; i<VertexNum; i++ ){ if(g.ver[i] == a){ st = i; } if(g.ver[i] == b){ en = i; } } for( i=0; i<VertexNum; i++ ){ used[i] = 0; parent[i] = 0; distance[i] = g.edg[st][i];//初始化为与编号为0的顶点的距离 } used[st] = 1; for( i=0; i<VertexNum-1; i++ ){ j = 0; mini = INFINITY; for( m=0; m<VertexNum; m++ ){ if((0 == used[m]) && (distance[m] < mini) ) { mini = distance[m]; j = m; //j为刚刚找到的V-U中到源点路径最短的顶点 } } used[j] = 1; for( n=0; n<VertexNum; n++ ){ if( (0 == used[n]) && (distance[n] > distance[j] + g.edg[j][n]) ){//由于有顶点新加入U集合,对距离数组distance进行更新, //比较原路径长度与以新加入的顶点为中间点的路径长度 distance[n] = distance[j] + g.edg[j][n]; parent[n] = j; } } } char ans [20]; int dq=st,dw=en; int pathnum=0; ans[pathnum++] = g.ver[dw]; while(g.ver[dw]!=g.ver[dq]){ dw=parent[dw]; ans[pathnum++] = g.ver[dw] ; } printf("\n路径是:"); for( i=pathnum-1; i>=0; i-- ){ if(i==0){ printf("%c", ans[i]); break; } else{ printf("%c ---> ", ans[i]); } } printf("\n\n"); if(distance[en]==666){ printf("无路径到达\n"); } else{ printf("%c到%c最短路径长度为: %d\n", g.ver[st], g.ver[en], distance[en]); } } int main(){ Graph g; freopen("in.txt","r",stdin);//切文件输入数据; CreateGraph( &g ); freopen("CON","r",stdin); //切换到从控制台输入数据; Dijkstra( g ); return 0; }
文件内容:
1234
0 1 -1 3
-1 0 2 4
-1 -1 0 5
-1 -1 -1 0
很经典,思想也很简单
原文:https://www.cnblogs.com/mvpmvp/p/14249564.html