最短路径问题一般分为两种情况,单源最短路径(即从一个点出发到其余各点的最短路径问题)和每对顶点之间的最短路径问题。Dijkstra和Floy算法相比之下我更喜欢Floy算法,该算法容易理解,思路简洁。
两种算法解决最短路径都是基于贪心的算法,从局部出发一点点扩展。
以一个简单的例子来说,如下图,图中有6点,可以看做是6个城市,求各城市之间的最短路径问题。这里图中只有6个顶点,你可能仅凭眼看就能看出来最短路径,就是一个暴力枚举解决的过程,但是当结点数增多,枚举效率是很慢很耗时的,还有如何将眼睛看得到最短路径的结果这个过程抽象成代码,然后编程解决,是很考察一个人的抽象能力、问题建模。
Dijkstra算法把这个图里面的所有顶点分成两个集合S和V-S,集合S中存放已经找到最短路径的顶点,V-S中存放当前还未找到最短路径的顶点。这里就体现贪心的思想了,当我去解决一个还未找到最短路径的顶点时,我可以在已经找到最短路径的集合里面找这些顶点是否有达到未找到最短路径的顶点的路径,这样子的走法是否比我直接走那个点近。算法按照最短路径长度递增的顺序逐个将V-S集合中的元素加入到S集合中来,直到所有的元素都加入到集合中,就完成了问题的求解。
下一个待解决顶点的最短路径只有两种可能:
1、直接从出发点到该点。
2、从出发点经过以求得最短路径的某个点,再到达待解决顶点,由两段路线组成。
这是这个算法的主要思想。大概的流程如下
核心算法:
void Dijkstra(const GRAPH *graph, int *dist) {
int min;
int k;
int j;
int t;
int i;
int *flag;
flag = (int *)calloc(sizeof(int), graph->vexnum);
for(j = 1, flag[0] = 1; j < graph->vexnum; j++) {
dist[j] = graph->arc[0][j];
}
for(t = 1; t < graph->vexnum; t++) {
min = INFINITY;
k = 0;
// 找到当前记录最短路径的数组dist 中最小的路径 还有达到的结点
for(i = 1; i < graph->vexnum; i++) {
if((flag[i] == 0) && (dist[i] < min)) {
min = dist[i];
k = i;
}
}
flag[k] = 1;
// 以这个找到的k结点 以这个结点为中间结点出发去判断 通过该节点达到其他结点是够更近
for(i = 1; i < graph->vexnum; i++) {
// 在这里如果发现了 从已经找到最短路径的节点间接达到待解决点 要更近如果有这样的路径
// 就更新记录最短路径的数组
if((flag[i] == 0) && (dist[k] + graph->arc[k][i] <= dist[i])) {
dist[i] = dist[k] + graph->arc[k][i];
}
}
}
}
Floy算法比起上面的算法理解起来要简单的多,该算法的时间复杂度是O(N^3),可以解决各顶点之间的最短路径。而Dijkstra解决的是一个顶点到其他顶点的问题,时间复杂度是O(N^2),若要用Dijkstra算法解决各顶点之间的最短路径问题,外层再加上一层循环,总的时间复杂度也是O(N^3)。所以两个算法时间复杂度都差不多。
FLoy算法引入一个和图一样大小的矩阵,叫做路由矩阵,来表示达到目标节点的前一个经过的结点。
这个算法的整体思想就是,比如现在有结点A,B,C三个结点,当以A为中间结点的时候,判断B->C还是B->A->C更近。然后继续以B,C为中间结点,做同样的操作。
算法过程如下。
核心代码:
void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix) {
int i;
int j;
int k;
for(i = 0; i < graph->vexnum; i++) {
for(j = 0; j < graph->vexnum; j++) {
Map_info[i][j] = graph->arc[i][j];
rount_matrix[i][j] = j;
}
}
for(i = 0; i < graph->vexnum; i++) {
for(j = 0; j < graph->vexnum; j++) {
for(k = 0; k < graph->vexnum; k++) {
if(Map_info[j][k] > Map_info[j][i] + Map_info[i][k]) {
Map_info[j][k] = Map_info[j][i] + Map_info[i][k];
rount_matrix[j][k] = i;
}
}
}
}
}
附上两个算法的完整代码和结果
#include<stdio.h>
#include<malloc.h>
#include"../../include/kwenarraytools.h"
#define MAXVEXNUM 100
#define INFINITY 99999
typedef struct GRAPH{
int vexnum; //顶点个数
int edgenum; //边的个数
int arc[MAXVEXNUM][MAXVEXNUM]; //图的邻接矩阵
char *vec_infor; //记录各个顶点的信息 长度和vexnum保持一致
}GRAPH;
void createGraph(GRAPH **graph, int vexnum, int edgenum);
void destoryGraph(GRAPH **graph);
void Dijkstra(const GRAPH *graph, int *dist);
void showMatrix_1(int array[][MAXVEXNUM], int n);
//map_info记录的各点到各点的距离信息, rout_matrix是路由矩阵 达到目标点最后一次经过的结点
void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix);
void showArray(int *ArrayFirstAddress, int array_length);
void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix) {
int i;
int j;
int k;
for(i = 0; i < graph->vexnum; i++) {
for(j = 0; j < graph->vexnum; j++) {
Map_info[i][j] = graph->arc[i][j];
rount_matrix[i][j] = j;
}
}
for(i = 0; i < graph->vexnum; i++) {
for(j = 0; j < graph->vexnum; j++) {
for(k = 0; k < graph->vexnum; k++) {
if(Map_info[j][k] > Map_info[j][i] + Map_info[i][k]) {
Map_info[j][k] = Map_info[j][i] + Map_info[i][k];
rount_matrix[j][k] = i;
}
}
}
}
}
void Dijkstra(const GRAPH *graph, int *dist) {
int min;
int k;
int j;
int t;
int i;
int *flag;
flag = (int *)calloc(sizeof(int), graph->vexnum);
for(j = 1, flag[0] = 1; j < graph->vexnum; j++) {
dist[j] = graph->arc[0][j];
}
for(t = 1; t < graph->vexnum; t++) {
min = INFINITY;
k = 0;
// 找到当前记录最短路径的数组dist 中最小的路径 还有达到的结点
for(i = 1; i < graph->vexnum; i++) {
if((flag[i] == 0) && (dist[i] < min)) {
min = dist[i];
k = i;
}
}
flag[k] = 1;
// 以这个找到的k结点 以这个结点为中间结点出发去判断 通过该节点达到其他结点是够更近
for(i = 1; i < graph->vexnum; i++) {
// 在这里如果发现了 从已经找到最短路径的节点间接达到待解决点 要更近如果有这样的路径
// 就更新记录最短路径的数组
if((flag[i] == 0) && (dist[k] + graph->arc[k][i] <= dist[i])) {
dist[i] = dist[k] + graph->arc[k][i];
}
}
}
}
void destoryGraph(GRAPH **graph) {
if((*graph) == NULL) {
return;
}
free((*graph)->vec_infor);
free(*graph);
*graph = NULL;
}
void createGraph(GRAPH **graph, int vexnum, int edgenum) {
int i;
int data;
int from;
int to;
int j;
*graph = (GRAPH *)calloc(sizeof(GRAPH), 1);
(*graph)->vexnum = vexnum;
(*graph)->edgenum = edgenum;
(*graph)->vec_infor = (char *)calloc(sizeof(char), 1);
for(i = 0; i < vexnum; i++) {
printf("input the %d/%d vex infor: ", i+1, vexnum);
setbuf(stdin, NULL);
(*graph)->vec_infor[i] = getchar();
}
for(i = 0; i < vexnum; i++) {
for(j = 0; j < vexnum; j++) {
(*graph)->arc[i][j] = INFINITY;
}
}
for(i = 0; i < vexnum; i++) {
(*graph)->arc[i][i] = 0;
}
for(i = 0; i < edgenum; i++) {
printf("input the %d/%d ede infor(from to power):", i+1, edgenum);
setbuf(stdin, NULL);
scanf("%d%d%d", &from, &to, &data);
// printf("from = %d, to = %d, data = %d\n", from, to, data);
(*graph)->arc[from][to] = (*graph)->arc[to][from]= data;
}
}
int main(void) {
GRAPH *graph = NULL;
int *dij_dist;
int vexnum = 4; //顶点个数
int edgenum = 5; //边数
int **Floy_result_matrix;
int **Floy_rount_matrix;
printf("input vexnum and edgenum: ");
scanf("%d%d", &vexnum, &edgenum);
dij_dist = (int *)calloc(sizeof(int), vexnum);
createGraph(&graph, vexnum, edgenum);
printf("Map Matrix infor:\n");
showMatrix_1(graph->arc, vexnum);
Dijkstra(graph, dij_dist);
printf("Dijkstra result: \n");
showArray(dij_dist, vexnum); //这里打印的是第一个节点到其他各点的最短距离信息
Floy_result_matrix = getMatrix(vexnum, vexnum);
Floy_rount_matrix = getMatrix(vexnum, vexnum);
Floy(graph, Floy_result_matrix, Floy_rount_matrix);
printf("Floy result: \n");
showMatrix(&Floy_result_matrix[0][0], vexnum, vexnum);
showMatrix(&Floy_rount_matrix[0][0], vexnum, vexnum);
destoryMatrix(&Floy_result_matrix, vexnum);
destoryMatrix(&Floy_rount_matrix, vexnum);
destoryGraph(&graph);
free(dij_dist);
return 0;
}
void showMatrix_1(int array[][MAXVEXNUM], int n) {
int i;
int j;
for(i = 0; i < n; i++) {
for(j = 0; j < n; j++) {
// printf("%d ", array[i][j]);
printf("%6d ", *(*(array + i)+j));
}
printf("\n");
}
printf("\n");
}
/*
6 10
1
2
3
4
5
6
0 1 10
0 2 21
0 4 8
1 2 18
1 3 5
1 5 6
2 4 25
2 5 19
3 5 7
4 5 33
*/
自己写的矩阵输出 销毁函数 头文件中有用到
void showArray(int *ArrayFirstAddress, int array_length);
void showMatrix(int *MatrixFisrtAddress, int row, int col) {
int i;
int j;
for(i = 0; i < row; i++) {
for(j = 0; j < col; j++) {
printf("%5d", *(MatrixFisrtAddress++));
}
printf("\n");
}
printf("\n\n");
}
//销毁数组
void destoryArray(int **array);
void destoryMatrix(int ***array, int row) {
int i;
for(i = 0; i < row; i++) {
free((*array)[i]);
(*array)[i] = NULL;
}
}