有权图的单源最短路亦被称为Dijkstra算法(单源最短路径),首先要声明这个算法无法解决含有负值圈的图
显然最优解释围绕着红色的圈不停的兜圈,所以基本上所有的算法都挂掉了
排除掉这种情况之后
Dijkstra算法步骤如下:
G={V,E}1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值
若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
若不存在<V0,Vi>,d(V0,Vi)为∞
2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中(每次从未被收录的顶点中选一个dist最小的收录 (贪心))
3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值
重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止
可以得到证明的是:
若路径是按照递增(非递减)的顺序生成的,则
真正的最短路必须只经过S中的顶点
如果收录v使得s到w的路径变短,则s到w的路径一定经过v,并且v到w有一条边
增加一个v进入S,可能影响另一个w的dist值,w只可能是v一圈的点
下面我们对于下面的有权图举个例子
邻接矩阵的Dijkstra算法实现
下面是伪代码描述以及矩阵的初始化
其函数的C语言描述如下
/* IsEdge(Graph, V, W)检查<V, W>是否图Graph中的一条边,即W是否V的邻接点。 */
/* 此函数根据图的不同类型要做不同的实现,关键取决于对不存在的边的表示方法。*/
/* 例如对有权图, 如果不存在的边被初始化为INFINITY, 则函数实现如下: */
bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
return Graph->G[V][W] <INFINITY ? true : false;
}
/*邻接矩阵储存——有权图的单源最短路算法*/
Vertex FindMinDist(MGraph Graph) {
/*返回未被收录顶点中的最小dist者*/
Vertex MinV, V;
int MinDist = INFINITY;
for (V = 0; V < Graph->Nv; V++) {
if (Visited[V] == false && dist[V] < MinDist) {
/*若V未被收录,且dist[V]更小*/
MinDist = dist[V];/*更新最小距离*/
MinV = V;/*更新最小顶点*/
}
}
if (MinDist < INFINITY)/*若找到最小dist*/
return MinV;/*返回对应的顶点下标*/
else return ERROR;/*若这样的顶点不存在,返回错误标记*/
}
/* dist[]和path[]为全局变量,已经初始化为-1 */
bool Dijkstra(MGraph Graph, Vertex S)
{ /* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索 */
Vertex V, W;
/*初始化:此处默认邻接矩阵中不存在的边用INFINITY表示*/
for (V = 0; V < Graph->Nv; V++) {
dist[V] = Graph->G[S][V];
if (dist[V] < INFINITY)
path[V] = S;
else
path[V] = -1;
Visited[V] = false;
}
/*先将起点收入集合*/
dist[S] = 0;
Visited[S] = true;
while (1)
{
/*找到未被收入顶点中dist最小者*/
V = FindMinDist(Graph);
if (V == ERROR)/*若这样的V不存在*/
break;//算法结束
Visited[V] = true;//收录V
for (W = 0; W < Graph->Nv; W++)//对图中每个顶点W
if (Visited[W] == false && Graph->G[V][W] < INFINITY) {//若W未被收录并且是V的临近点
if (Graph->G[V][W] < 0)//若有负边
return false;//不能正确解决,返回错误标记
if (dist[V] + Graph->G[V][W] < dist[W])//若收录V使得dist[W]变小
{
dist[W] = dist[V] + Graph->G[V][W];//更新dist[W]
path[W] = V;//更新S到W的路径
}
}
}/*while结束*/
return true;/*算法执行完毕,返回正确标记*/
}
执行结果嘿嘿,当然没毛病咯
和我们通过肉眼看的结果一样一样的
附上完整代码
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define INFINITY 65535 /* ∞设为双字节无符号整数的最大值65535*/
#define MaxVertexNum 100 /* 最大顶点数设为100 */
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
typedef int WeightType; /* 边的权值设为整型 */
typedef char DataType; /* 顶点存储的数据类型设为字符型 */
int Visited[MaxVertexNum];
int dist[MaxVertexNum];
int path[MaxVertexNum];
/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode {
Vertex V1, V2; /* 有向边<V1, V2> */
WeightType Weight; /* 权重 */
};
typedef PtrToENode Edge;
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode {
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
MGraph CreateGraph(int VertexNum)
{ /* 初始化一个有VertexNum个顶点但没有边的图 */
Vertex V, W;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图 */
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接矩阵 */
/* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
for (V = 0; V<Graph->Nv; V++)
for (W = 0; W<Graph->Nv; W++)
Graph->G[V][W] = INFINITY;
return Graph;
}
void InsertEdge(MGraph Graph, Edge E)
{
/* 插入边 <V1, V2> */
Graph->G[E->V1][E->V2] = E->Weight;
/* 若是无向图,还要插入边<V2, V1> */
//Graph->G[E->V2][E->V1] = 1;
}
/* IsEdge(Graph, V, W)检查<V, W>是否图Graph中的一条边,即W是否V的邻接点。 */
/* 此函数根据图的不同类型要做不同的实现,关键取决于对不存在的边的表示方法。*/
/* 例如对有权图, 如果不存在的边被初始化为INFINITY, 则函数实现如下: */
bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
return Graph->G[V][W] <INFINITY ? true : false;
}
/*邻接矩阵储存——有权图的单源最短路算法*/
Vertex FindMinDist(MGraph Graph) {
/*返回未被收录顶点中的最小dist者*/
Vertex MinV, V;
int MinDist = INFINITY;
for (V = 0; V < Graph->Nv; V++) {
if (Visited[V] == false && dist[V] < MinDist) {
/*若V未被收录,且dist[V]更小*/
MinDist = dist[V];/*更新最小距离*/
MinV = V;/*更新最小顶点*/
}
}
if (MinDist < INFINITY)/*若找到最小dist*/
return MinV;/*返回对应的顶点下标*/
else return ERROR;/*若这样的顶点不存在,返回错误标记*/
}
/* dist[]和path[]为全局变量,已经初始化为-1 */
bool Dijkstra(MGraph Graph, Vertex S)
{ /* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索 */
Vertex V, W;
/*初始化:此处默认邻接矩阵中不存在的边用INFINITY表示*/
for (V = 0; V < Graph->Nv; V++) {
dist[V] = Graph->G[S][V];
if (dist[V] < INFINITY)
path[V] = S;
else
path[V] = -1;
Visited[V] = false;
}
/*先将起点收入集合*/
dist[S] = 0;
Visited[S] = true;
while (1)
{
/*找到未被收入顶点中dist最小者*/
V = FindMinDist(Graph);
if (V == ERROR)/*若这样的V不存在*/
break;//算法结束
Visited[V] = true;//收录V
for (W = 0; W < Graph->Nv; W++)//对图中每个顶点W
if (Visited[W] == false && Graph->G[V][W] < INFINITY) {//若W未被收录并且是V的临近点
if (Graph->G[V][W] < 0)//若有负边
return false;//不能正确解决,返回错误标记
if (dist[V] + Graph->G[V][W] < dist[W])//若收录V使得dist[W]变小
{
dist[W] = dist[V] + Graph->G[V][W];//更新dist[W]
path[W] = V;//更新S到W的路径
}
}
}/*while结束*/
return true;/*算法执行完毕,返回正确标记*/
}
int main() {
MGraph Graph = CreateGraph(7);
Graph->Ne = 12;
Vertex a[12] = { 0,0,1,1,2,2,3,3,3,3,4,6 };
Vertex b[12] = { 1,3,3,4,0,5,2,4,5,6,6,5 };
WeightType w[12] = { 2,1,3,10,4,5,2,2,8,4,6,1 };
/* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
for (int i = 0; i<Graph->Ne; i++) {
Edge E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点 */
E->V1 = a[i]; E->V2 = b[i]; E->Weight = w[i];
/* 注意:如果权重不是整型,Weight的读入格式要改 */
InsertEdge(Graph, E);
}
for (int i = 0; i < MaxVertexNum; i++) { Visited[i] = false; dist[i] = INFINITY; path[i] = -1; }
Dijkstra(Graph, 0);
int i = 0;
for (int i = 0; i < Graph->Nv; i++) {
printf("点%d到点1的最小距离是:%d 上一步结点是:%d\n", i + 1, dist[i], path[i] + 1);
}
system("pause");
}