Loading

最短路径问题

最短路径问题的抽象

在网络中,求两个不同顶点之间的所有路径中,边的权值之和最小的那一条路径。

  • 这条路径就是两点之间的最短路径
  • 第一个顶点为源点
  • 最后一个顶点为终点

问题分类

单源最短路径问题:从某固定源点出发,求其到所有其他顶点的最短路径。

  • (有向)无权图
  • (有向)有权图
    多源最短路径问题:求任意两顶点之间的最短路径

无权图的单源最短路径算法

按照递增(非递减)的顺序找出到各个顶点的最短路,类似BFS。

void Unweighted(LGraph Graph, int dist[], int path[], Vertex S){		//dist[]和path[]全部初始化为-1
	Queue Q;
	Vertex V;
	PtrToAdjVNode W;

	Q=CreateQueue(Graph->Nv);		//创建空队列
	dist[S]=0;		//S为源点
	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;		//S到W->AdjV的距离更新
				path[W->AdjV]=V;		//将V记录在S到W->AdjV的路径上
				AddQ(Q,W->AdjV);
			}
	}
}

时间复杂度T=O(|V|+|E|)

有权图的单源最短路径算法

按照递增的顺序找出到各个顶点的最短路径,我们使用Dijkstra算法。

  • Dijkstra算法
    • 令S=
    • 对任一未收录的顶点v,定义dist[v]为s到v的最短路径长度,但该路径仅经过S中的顶点。即路径{s->(vi)->v}的最小长度
    • 若路径是按照递增(非递减)的顺序生成的,则
      • 真正的最短路径必须只经过S中的顶点
      • 每次从未收录的顶点中选一个dist最小的收录(贪心)
      • 增加一个v进入S,可能影响另外一个w的dist值
        • dist[w]=min
Vertex FindMinDist(MGraph Graph, int dist[], int collected[]){		//返回未被收录顶点中dist最小者
	Vertex MinV,V;
	int MinDist=INFINITY;

	for(V=0;V<Graph->Nv;V++){
		if(collected[V]==false&&dist[V]<MinDist){		//若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;

	for(V=0;V<Graph->Nv;V++){		//初始化,默认邻接矩阵中不存在的边用INFINITY表示
		dist[V]=Graph->G[S][V];
		if(dist[V]<INFINITY)
			path[V]=S;
		else
			path[V]=-1;
		collected[V]=false;
	}

	dist[S]=0;		//源点S
	collected[S]=true;

	while(1){
		V=FindMinDist(Graph, dist, collected);		//找到未被收录顶点中dist最小者V
		if(V==ERROR)
			break;
		collected[V]=true;		//收录V
		for(W=0;W<Graph->Nv;W++)		//对图中的每个顶点W
			if(collected[W]==false&&Graph->G[V][W]<INFINITY){		//若W未被收录且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的路径
				}
			}
	}
	return true;
}

多源最短路径算法

对于多源最短路径算法,有两种方法

  • 方法1:直接将单源最短路径算法调用|V|遍
    • T=O(|V|^3+|E|*|V|)
    • 对于稀疏图效果比较好
  • 方法2:Floyd算法
    • T=O(|V|^3)
    • 对于稠密图效果比较好

在这里我们介绍Floyd算法

  • Floyd算法
    • Dk[i][j]=路径{i->{l<=k}->j}的最小长度
    • D0,D1,···,D|V|-1[i][j]即给出了i到j的真正最短距离
    • 当Dk-1已经完成,递推到Dk时:
      • 如果k不在最短路径{i->{l<=k}->j}里面,则Dk=Dk-1
      • 如果k在最短路径{i->{l<=k}->j}里面,则该路径必定由两段最短路径组成:Dk[i][j]=Dk-1[i][k]+Dk-1[k][j]
bool Floyd(MGraph Graph, WeightType D[][MaxVertexNum], Vertex path[][MaxVertexNum]){
	Vertex i,j,k;

	for(i=0;i<Graph->Nv;i++)		//初始化
		for(j=0;j<Graph->Nv;j++){
			D[i][j]=Graph->G[i][j];
			path[i][j]=-1;
		}

	for(k=0;k<Graph->Nv;k++)
		for(i=0;i<Graph->Nv;i++)
			for(j=0;j<Graph->Nv;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;
}
posted @ 2020-07-05 18:47  Kinopio  阅读(114)  评论(0编辑  收藏  举报