课堂笔记:最短路径

非网图中,最短路径是指两顶点之间经历的边数最少的路径
网图中,最短路径是指两顶点之间经历的边上权值之和最短的路径
单源点到其他顶点的最短路径:Dijkstra方法,O(n2)
任意一对顶点之间的最短路径:Floyed方法,O(n3)
单源点最短路径问题
问题描述:给定带权有向图G=(V, E)和源点v∈V,求从v到G中其余各顶点的最短路径。
应用实例——计算机网络传输的问题:怎样找到一种最经济的方式,从一台计算机向网上所有其它计算机发送一条消息。
迪杰斯特拉(Dijkstra)提出了一个按路径长度递增的次序产生最短路径的算法——Dijkstra算法。
路径长度递增的理解
含有n个顶点的图,计算图中顶点v到其他顶点(n-1个)的最短路,总共要找n-1条最短路。
按路径长度递增指的是这n-1条路的计算原则即, 先找第一条最短路(v,vi),所有n-1条路中最短的路,再找第二条最短路(v,vj)…
Dijkstra算法
基本思想:

1、设置一个集合S存放已经找到最短路径的顶点,S的初始状态只包含源点v,
2、对vi∈V-S,假设从源点v到vi的有向边为最短路径(从v到其余顶点的最短路径的初值)。
3、以后每求得一条最短路径v, …, vk,就将vk加入集合S中,并将路径v, …, vk , vi与原来的假设相比较,取路径长度较小者为最短路径。
重复上述过程,直到集合V中全部顶点加入到集合S中。
路径长度最短的最短路径(即第一条最短路)的特点
在这条路径上,必定只含一条边,并且这条边上的权值最小。
下一条路径长度次短的最短路径的特点
它只可能有两种情况: 或者是直接从源点到该点(只含一条边); 或者是从源点经过顶点v1(第一条最短路径所依附的顶点),再到达该顶点(由两条边组成)。
再下一条路径长度次短的最短路径的特点:
它可能有四种情况:或者是直接从源点到该点(只含一条边); 或者从源点经过顶点v1,再到达该顶点(由两条边组成);或者是从源点经过顶点v2,再到达该顶点(两条条边);或者是从源点经过顶点v1、v2,再到达该顶点(多条边)。
其余最短路径的特点
它或者是直接从源点到该点(只含一条边); 或者是从源点经过已求得最短路径的顶点(集合S中的顶点),再到达该顶点。
正确性证明
下一条最短路径或者是弧(v0,vx),或者是中间经过S中的某些顶点而后到达vx的路径。 
用反证法: 假设下一条最短路径上有一个顶点vy不在S中, 即此路径为(v0,…,vy,…,vx)。 显然,(v0,…,vy)的长度小于(v0,…,vy,…,vx)的长度, 故下一条最短路径应为(v0,…,vy), 这与假设的下一条最短路径(v0,…,vy,…, vx)相矛盾! 因此,下一条最短路径上不可能有不在S中的顶点vy,即假设不成立。
数据结构 :
图的存储结构:邻接矩阵存储结构
数组dist[n]:每个分量dist[i]表示当前所找到的从始点v到终点vi的最短路径的长度。初态为: 若从v到vi有弧,则dist[i]为弧上权值;否则置dist[i]为∞。
数组path[n]:path[i]是一个字符串,表示当前所找到的从始点v到终点vi的最短路径。初态为:若从v到vi有弧,则path[i]为vvi;否则置path[i]空串。
数组s[n]:存放源点和已经找到最短路径的终点,其初态为只有一个源点v。
迪杰斯特拉算法的主要步骤如下
(1) g为用邻接矩阵表示的带权图。 S←{v0} , dist[i]=g.arcs[v0][vi],path[i]=“v0vi”或“”;将v0到其余顶点的路径长度初始化为权值;
(2) 选择vk,使得dist[vk]=min(dist[i] | vi∈V-S) ,vk为目前求得的下一条从v0出发的最短路径的终点。 将vk加入到S中
(3) 修改从v0出发到集合V-S上任一顶点vi的最短路径的长度。如果 dist[k]+ g.arcs[k][i]<dist[i] 则将dist[i]修改为 dist[k]+ g.arcs[k][i] path[i]=path[k]+“vi”
(4) 重复(2)、(3) n-1次,即可按最短路径长度的递增顺序,逐个求出v0到图中其它每个顶点的最短路径。

const int MAX=1000; 
void  Dijkstra(MGraph g, int v){        
	for ( i =0; i<g.vexnum ; i++){   
		dist[i]=g.arcs[v][i];                  
		if ( dist[i]!= MAX)                        
			path [i]=g.vertex[v]+g.vertex[i];
		else                       
			path[i]=“”;        
	}        
	S[0]=g.vertex[v];         
	num=1;  
	While (num<g.vextexNum){     
		k=0;     
		for(i=0;i<G.vertexNum;i++)            
			if((dist[i]<dist[k])   
				k=i     
		cout<<dist[k]<<path[k];     
		s[num++]=G.vertex[k];
		for(i=0;i<G.vertexNum;i++)              
			if(dist[k]+g.arc[k][i]<dist[i] {    
				dist[i]=dist[k]+g.arc[k][i];                        
				path[i]=path[k]+g.vertex[i];                
			} 
	} 
}             

每一对顶点之间的最短路径
问题描述:给定带权有向图G=(V, E),对任意顶点vi,vj∈V(i≠j),求顶点vi到顶点vj的最短路径。
解决办法1:每次以一个顶点为源点,调用Dijkstra算法n次。显然,时间复杂度为O(n3)。
解决办法2:弗洛伊德提出的求每一对顶点之间的最短路径算法——Floyd算法,其时间复杂度也是O(n3),但形式上要简单些。
Floyd算法的基本思想如下
设图g用邻接矩阵法表示,求图g中任意一对顶点vi、 vj间的最短路径。 (-1) 将vi到vj 的最短的路径长度初始化为(vi,vj), 然后进行如下n次比较和修正:
(0) 在vi、vj间加入顶点v0,比较(vi, v0, vj)和(vi, vj)的路径的长度,取其中较短的路径作为vi到vj的且中间顶点号不大于0的最短路径。
(1) 在vi、vj间加入顶点v1,得(vi, …,v1)和(v1, …,vj),其中:(vi, …, v1)是vi到v1 的且中间顶点号不大于0的最短路径,(v1, …, vj) 是v1到vj 的且中间顶点号不大于0的最短路径,这两条路径在上一步中已求出。将(vi, …, v1, …, vj)与上一步已求出的且vi到vj 中间顶点号不大于0的最短路径比较,取其中较短的路径作为vi到vj 的且中间顶点号不大于1的最短路径。
(2) 在vi、vj间加入顶点v2,得(vi, …, v2)和(v2, …, vj), 其中:(vi, …, v2)是vi到v2 的且中间顶点号不大于1的最短路径,(v2, …, vj)是v2到vj 的且中间顶点号不大于1的最短路径,这两条路径在上一步中已求出。将(vi, …, v2, …, vj)与上一步已求出的且vi到vj 中间顶点号不大于1的最短路径比较, 取其中较短的路径作为vi到vj 的且中间顶点号不大于2的最短路径。
……
设计数据结构
图的存储结构:带权的邻接矩阵存储结构
数组dist[n][n]:存放在迭代过程中求得的最短路径长度。迭代公式
dist-1[i][j]=arc[i][j],
dist k[i][j]=min{distk-1[i][j], distk-1[i][k]+distk-1[k][j]}
0≤ k ≤n-1
数组path[n][n]: 存放从vi到vj的最短路径,初始为path[i][j]=“vivj”。
Floyd算法——C++描述

void Floyd(MGraph G) 
{     
	for (i=0; i<G.vertexNum; i++)                
		for (j=0; j<G.vertexNum; j++)        
		{           
			dist[i][j]=G.arc[i][j];           
			if (dist[i][j]!=)                 
				path[i][j]=G.vertex[i]+G.vertex[j];           
			else 
				path[i][j]="";
		}
		for (k=0; k<G.vertexNum; k++)                  
			for (i=0; i<G.vertexNum; i++)                   
				for (j=0; j<G.vertexNum; j++)                
					if (dist[i][k]+dist[k][j]<dist[i][j]) {
						dist[i][j]=dist[i][k]+dist[k][j];                     
						path[i][j]=path[i][k]+path[k][j];               
					} 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值