图里面的最短路径

最短路径对应的现实问题

计算机网络中路由算法其实对应的就是最短路径问题,从一个结点到另一个结点,怎么样的走才是最小代价。

从某顶点都其余顶点的最短路径(迪杰斯特拉 Dijkstra)

这里讨论的问题是,给定一个带权的有向图G与源点v,求从v到G中其余顶点的最短路径。
在这里插入图片描述

算法的思想
假设图有n个顶点,那么就有n-1条最短路径,因为从某个点开始到其余的n-1个点都有一条最短路径。迪杰斯特拉提出了按照路径长度的递增顺序求得最短路径,也就是先求得这n-1条路径里面最短的那一条路径,然后再求得第二短的那条最短路径,直到求出n-1条最短路径。
假设有一个最短路径结点的集合S,设当前的S={V0},从V0开始,每求得一条最短路径,(V0,…,Vk),就将Vk加入到S集合中,直到全部顶点都被加到集合中。

性质:
假设S是已经求得的最短路径的终点的集合,那么可以证明下一条最短路径(假设这条路径的终点为Vk),肯定是(V0,Vk)或者是经过集合S中顶点便可到达Vk的路径。(通俗的解释就是,下一条路径要不就是V0直达终点Vk,要不就是V0通过S中的随意个顶点进行跳转然后达到Vk)。

性质的证明:反证法,假设在生成(V0,…,Vp,…,Vk)这条最短路径中有一个顶点Vp,如果这个Vp不在集合S中,并且有(V0…Vp)要比(V0,…,Vk)要短。但是这个是不可能的,因为我们生成最短路径的时候已经是长度的递增顺序开始的,因此比这条最短路径长度要短的都已经生成了,而且它们的终点都已经存在集合S中了。

在这里插入图片描述

解释:
利用上图并结合性质来理解寻找最短路径,首先S={V0},从V0开始,最短的最短路径是可能是(V0,V1),(V0,V3)(V0,V4),对比这三条,发现最短的是(V0,V3),所以V3进入集合S。到V3最短路径为10。

S={V0,V3}接着找第二短的最短路径,可能是V0直达的(V0,V1),(V0,V4),或者是经过V3跳转的(V0,V3,V4),通过对比发现,最短的那条应该是(V0,V1),所以V1入S集合。到V3最短路径为30。

S={V0,V3,V1},接着找第三短的最短路径,可能是(V0,V4)或者是经过V3跳转的(V0,V3,V4),经过V1跳转的(V0,V1,V4),(V0,V1,V2),对比发现(V0,V3,V4)最短的,所以V4如S集合。到V4最短路径为10+30=40,所以可以发现对于顶点V4,其实已经把错误的路径给排除掉了。
…一直找到所有的顶点都入S了,就说明n-1条最短路径已经找完了。

已经知道利用性质如何人工查找最短路径,那么现在就是把人工做的操作交给电脑进行实现。
代码如下:
在这里插入图片描述

int S[MaxVertices];//存放已经求出最短路径的顶点
int path[MaxVertices];//该条最短路径上该顶点的前一个顶点
int dist[MaxVertices];//存放从顶点V0到其他顶点的当前的最短路径。dist存放的当前路径的最短路径不一定是真正的最短路径,只有该顶点已经入S的情况下,它对应的dist里面的值才是正确的。

void shorttesstPath_Dijkstra(AdjMatrix G,int v){  //G是具有n个顶点的带权有向图,求源点V到其余各顶点的最短路径
	for(int i=0;i<G.num;i++){  //G.num是顶点个数
		dist[i]=G.Edge[v][i];  //G.Edge[][] 是存储边的邻接矩阵
		s[i]=0;
		if(i!=v&&dist[i]!=MAXNUM){//MAXNUM是正无穷,也就是两点无直接相连的边或者是自己到自己
			path[i]=v;
		}else{
			path[i]=-1;
		}
	}
	s[v]=1;//代表V点已经入S
	dist[v]=0;//代表自己到自己路径为0
	for(i=0;i<G.num-1;i++){//求第i+1条边
		float min=MAXNUM;
		int u=v;
		for(int j=0;j<n;j++){
			if(!s[j]&&dist[j]<min){ u=j;min=dist[j]}//找到当前剩余最短路径中的最短路径
		}
		s[u]=1;//将u入s
		for(int w=0;w<G.num;w++){//当新的顶点入S的时候,跟新顶点直接相连的顶点的最短路径可能会发生改变
		 if(!S[w]&&G.Edge[u][w]<MAXNUM&&G.Edge[u][w]+dist[u]<dist[w])
		 {dist[w]=G.Edge[u][w]+dist[u];
	      path[w]=u;
		 }
		}
	}

}

图中每对顶点的最短路径

刚刚已经知道了从v0到途中其他顶点的最短路径,那么如果采用迪杰斯特拉算法的话,只要将图中的每个顶点都运行一遍,那么就能知道图中每一对最短路径,和最短路径长度。

但是还有一种比这个方法更简单的算法,就是弗洛伊德(Floyd)算法。有向权图,还是用邻接矩阵Edge[n][n]来表示,设置一个nXn的一个方阵dist[n][n],初始时对角线的值为0,代表自己到自己为0。其他的元素dist[i][j]与Edge[i][j]的值相同,值的含义为i点到j点的最短路径长度。该路径不一定是最短路径,还需要进行n次的试探。首先考虑V0点作为中间顶点,判断(Vi,V0,Vj)是否存在,如果存在取(Vi,V0,Vj)和(Vi,Vj)中小的那一条,存入到dist[i][j]。然后可以考虑增加V1当做中间顶点,由于dist[i][1]和dist[1][j]分别是Vi到 V1 , V1 到Vj经过中间顶点不大于0顶点的最短距离,所以只要找dist[i][1]+dist[1][j] 和dist[i][j]中的最小者存入dist[i][j]。继续考虑增加…直到考虑所有的顶点之后,d[i][j]就是i到j的最短路径了。

按照上述的逻辑,算法如下

void shorttesstPath_Floyd(AdjMatrix G){ 
 for(int i=0;i<G.num;i++)
 { for(int j=0;j<G.num;j++)
	 { if(i==j){dist[i][j]=0;} //对角线置零
       else {dist[i][j]=G.Edge[i][j]} 
	   
       if(i<>j&&dist[i][j]<MAXNUM){ path[i][j]=i;}//i和j有边,对这条边而言,终点j的前一个顶点是i
       else { path[i][j]=-1;}//没有边或者是自己的情况,没有前一个顶点
	 }
 }
 
 
 for(int k=0;k<G.num,k++)//逐渐增加中间顶点
	 for(int i=0;i< G.num,i++) //遍历整个dist
		 for(int j=0;i< G.num,j++)
		 { if(dist[i][k]+dist[k][j]<dist[i][j]) //对于每个ij而言,如果有中间顶点加入路径更小,
		   {dist[i][j]=dist[i][k]+dist[k][j];//就更新dist[i][j]
	        path[i][j]=path[k][j]; //对于i和j的边,更新终点j的前一个顶点
		   }
		 }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值