什么是最短路径:
图中的某一个节点到另外某一个节点的最短路径的权值和,如果是无权值的图,则理解为权值为1的图。
图的邻接矩阵结构:
要计算最短路径,首先需要一个图,这里用邻接矩阵来表示一个图。
typedef char VertexType; //顶点类型 ,默认为char
typedef int EdgeType; //边的权值 默认int
#define MAXVERTEX 10 //最大的顶点数 默认为10
#define INFINITY 65535 //表示无尽∞
//邻接矩阵的结构
struct MGraph
{
VertexType vexs[MAXVERTEX]; //顶点数组
EdgeType arc[MAXVERTEX][MAXVERTEX]; //邻接矩阵 即边数组
int vertexNum; //顶点数
int edgeNum; //边数
};
Dijkastra算法:
Dijkastra算法把一个问题分解成小问题的集合,比如要计算V0~V9的最短路径,先计算V0~V1的最短路径,然后到V2,最终计算到V9的最短路径。
typedef int ShortPathWeight[MAXVERTEX]; //存储到各个顶点最短路径的权值
typedef int PathPercursor[MAXVERTEX]; //存储某个顶点最短路径的前驱顶点,如PathPercursor[v8]=7,则到v8的最短路径前一个是v7点
//Dijkastra算法最短路径
void DijkastraShortestPath(MGraph G, int v0, ShortPathWeight *S, PathPercursor *P)
{
int i, j; //用于循环
int min; //用于记录最短权值来比较
int k; //用于记录最小权值顶点下标
int OverVertex[MAXVERTEX]; //用于记录已经求出最短路径的顶点,当数组值为0表示未求出,当数组值为1表示已经求出
//初始化要使用的数组
for (i = 0; i < G.vertexNum; i++)
{
OverVertex[i] = 0; //默认没有求出任何点
(*S)[i] = G.arc[0][i]; //把邻接矩阵的第一行数据记录到ShortPath数组
(*P)[i] = 0; //路径数组初始化为0
}
OverVertex[v0] = 1; //源点V0不需要求,默认1
(*S)[v0] = 0; //V0到自身V0距离为0
//循环遍历每一个顶点,来求出V0到每一个顶点vi的最短路径
for (i = 1; i < G.vertexNum; i++) //i从1开始遍历
{
//找出ShortPathWeight的最小值
min = INFINITY; //默认最小权值为不可能数
for (j = 0; j < G.vertexNum; j++)
{
if (OverVertex[j] != 1 && (*S)[j] < min) //找出到顶点的最小值
{
k = j;
min = (*S)[j]; //k顶点离源点最近,最小权值为min
}
}
OverVertex[k] = 1; //把k顶点的标识符设为true
//在已经找到k顶点为最短路径的基础上,循环寻找与K顶点连接的后面顶点的最短路径
for (j = 0; j < G.vertexNum; j++)
{
//经过K顶点到某一顶点的路径比v0直接到某一顶点的路径短
if (OverVertex[j] != 1 && (min + G.arc[k][j]) < (*S)[j])
{
(*S)[j] = min + G.arc[k][j];
(*P)[j] = k;
}
}
}
}
用了两个数组分别记录到某点的最短路径权值和前驱顶点,另外内部用一个标识符来表示是否已经求出某点的最短路径。
Floyd算法:
Floyd非常巧妙,它设定了一个中间顶点,比如要计算V0~V2的权值,中间顶点为V1:比较(V0~V2)和(V0~V1+V1~V2)的值,把较小的值记录为最短权值。
typedef int ShortPathWeightMatrix[MAXVERTEX][MAXVERTEX]; //任意顶点到任意顶点最短路径的权值矩阵
typedef int PathPercursorMatrix[MAXVERTEX][MAXVERTEX]; //任意顶点到任意顶点最短路径的前驱顶点矩阵
//Floyd算法最短路径
void FloydShortestPath(MGraph G, ShortPathWeightMatrix *S, PathPercursorMatrix *P)
{
int i, j, k; //用于循环
//初始化数组
for (i = 0; i < G.vertexNum; i++)
{
for (j = 0; j < G.vertexNum; j++)
{
(*S)[i][j] = G.arc[i][j]; //把S数组初始化为G的邻接矩阵
(*P)[i][j] = j; //把P数组初始化为0-VertexNum的矩阵,即最短路径前驱为自身
}
}
//三重循环嵌套,k为路径中间经过点
for (k = 0; k < G.vertexNum; k++)
{
for (i = 0; i < G.vertexNum; i++)
{
for (j = 0; j < G.vertexNum; j++)
{
//找到Vi到Vj的权值大于(Vi~Vk)+(Vk~Vj)的点
if ((*S)[i][j] > (*S)[i][k] + (*S)[k][j])
{
//改变记录的最短路径权值
(*S)[i][j] = (*S)[i][k] + (*S)[k][j];
//把某点的最短路径前驱顶点改为K
(*P)[i][j] = (*P)[i][k];
}
}
}
}
}
算法的时间复杂度:
Dijkastra算法用了两重嵌套循环,所以时间复杂度为O(n^2);
Floyd算法用了三重嵌套循环,所以时间复杂度为O(N^3)。