1、最短路径问题
单源多源、有权无权单源:固定起点,计算到其他每个顶点的最短路径,又分无权图 && 有权图
多源
2、无权图的单源最短路算法
1)思想:
路径长度为0的:起点
路径长度为1的:……
路径长度为2的:……
直到全部顶点都被访问到。
BFS遍历一次自然形成这个结果!
2)具体算法(类似BFS:访问入队-取元素如果子元素未访问,访问入队)第1个元素先访问再入队
此后,每从队列取元素,遍历邻接点,如果未访问,则先访问再入队
注意:出队的时候,出队元素已经访问过,所以要操作的是邻接点!
void UnWeighted(Vertex S){
dist[S] = 0;
Enqueue(Q, V);
while(!isEmpty(Q)):
{
Vertex V=DeQueue(Q);
for (V的每个邻接点W){
if (dist[V] == -1)
{
dist[W] = dist[V] + 1;
path[W] = V;#上一个节点
Enqueue(Q, W);
}
}
}
}
dist[W]和path[W] 分别记录距离和路径。
初始化:除开起点dist[W]=-1(不可能的值)
3)时间复杂度
如果是邻接表表示,O(V+E)
(拆成2部分看:enqueue和dequeue是一部分,其余是另一部分:每个V入队1次,出队1次;for (V的每个邻接点W),enqueue和dequeue合起来O(2V),其余for执行O(E),而O(2V+E)= O(V+E)
如果是邻接矩阵表示,enqueue和dequeue合起来O(2V),其余for执行O(V^2),合起来O(2V+V^2)= O(V^2)
4)代码示例
/* 邻接表存储 - 无权图的单源最短路算法 */
/* dist[]和path[]全部初始化为-1 */
void Unweighted ( LGraph Graph, int dist[], int path[], Vertex S )
{
Queue Q;
Vertex V;
PtrToAdjVNode W;
Q = CreateQueue( Graph->Nv ); /* 创建空队列, MaxSize为外部定义的常数 */
dist[S] = 0; /* 初始化源点 */
AddQ (Q, S);
while( !IsEmpty(Q) ){
V = DeleteQ(Q);
for ( W=Graph->G[V].FirstEdge; W; W=W->Next ) /* 对V的每个邻接点W->AdjV */
if ( dist[W->AdjV]==-1 ) { /* 若W->AdjV未被访问过 */
dist[W->AdjV] = dist[V]+1; /* W->AdjV到S的距离更新 */
path[W->AdjV] = V; /* 将V记录在S到W->AdjV的路径上 */
AddQ(Q, W->AdjV);
}
} /* while结束*/
}
3、有权图的单源最短路算法:Dijkstra算法
不考虑负值圈
最短路径算法广泛用于网络路由协议,最着名的是IS-IS(中间系统到中间系统)和开放最短路径优先(OSPF)
1)思路:
1)贪心算法:每次取weight最小的边,加入集合S
2)dist(v)最小的路径一定不经过S外面的顶点(证明:假设v是下一个要加入集合的点,如果有另外一个点w在S之外,v经过w的距离才是最小,那么一定sw<=sv,v下一个被收入,w必须已经收入S,矛盾)
3)加入新节点只可能影响到邻接点的dist(如果收录v使得s到w的路径变短,则s到w的路径一定经过v,且v到w有一条边直接相连 证明:如果w的路径,在v之后还有v-k-w,则k的dist一定比v的大,v是下一个收录的节点,说明k一定没被收录 ,而按照算法w的路径经过的点一定已经收录在集合内)
2)时间复杂度:
主要看未收录中dist最小的顶点 ,这一步怎么实现
dist初始化分3种
源点:dist为0;
源点的邻接点:权重;
其他:正无穷(以便发现更小更新)
3)代码示例:
/* 邻接矩阵存储 - 有权图的单源最短路算法 */
Vertex FindMinDist( MGraph Graph, int dist[], int collected[] )
{ /* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;
int MinDist = INFINITY;
for (V=0; VNv; V++) {
if ( collected[V]==false && dist[V]
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else return ERROR; /* 若这样的顶点不存在,返回错误标记 */
}
bool Dijkstra( MGraph Graph, int dist[], int path[], Vertex S )
{
int collected[MaxVertexNum];
Vertex V, W;
/* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
for ( V=0; VNv; V++ ) {
dist[V] = Graph->G[S][V];
if ( dist[V]
path[V] = S;
else
path[V] = -1;
collected[V] = false;
}
/* 先将起点收入集合 */
dist[S] = 0;
collected[S] = true;
while (1) {
/* V = 未被收录顶点中dist最小者 */
V = FindMinDist( Graph, dist, collected );
if ( V==ERROR ) /* 若这样的V不存在 */
break; /* 算法结束 */
collected[V] = true; /* 收录V */
for( W=0; WNv; W++ ) /* 对图中的每个顶点W */
/* 若W是V的邻接点并且未被收录 */
if ( collected[W]==false && Graph->G[V][W]
if ( Graph->G[V][W]<0 ) /* 若有负边 */
return false; /* 不能正确解决,返回错误标记 */
/* 若收录V使得dist[W]变小 */
if ( dist[V]+Graph->G[V][W] < dist[W] ) {
dist[W] = dist[V]+Graph->G[V][W]; /* 更新dist[W] */
path[W] = V; /* 更新S到W的路径 */
}
}
} /* while结束*/
return true; /* 算法执行完毕,返回正确标记 */
}
4、多源图的最短路径
1)对每个顶点做起点调用一次单源
对稀疏图,Vlog V,可以
对稠密图:V(V^2+E)
稀疏图:E约等于V
稠密图:E>>>>V
2)floyd算法适合稠密图
标准的O(V^3),没有V(V^2+E)的尾巴三重循环,V^3
3)代码示例
/* 邻接矩阵存储 - 多源最短路算法 */
bool Floyd( MGraph Graph, WeightType D[][MaxVertexNum], Vertex path[][MaxVertexNum] )
{
Vertex i, j, k;
/* 初始化 */
for ( i=0; iNv; i++ )
for( j=0; jNv; j++ ) {
D[i][j] = Graph->G[i][j];
path[i][j] = -1;
}
for( k=0; kNv; k++ )
for( i=0; iNv; i++ )
for( j=0; jNv; j++ )
if( D[i][k] + D[k][j] < D[i][j] ) {
D[i][j] = D[i][k] + D[k][j];
if ( i==j && D[i][j]<0 ) /* 若发现负值圈 */
return false; /* 不能正确解决,返回错误标记 */
path[i][j] = k;
}
return true; /* 算法执行完毕,返回正确标记 */