单源最短路径(Dijkstra算法)

迪杰斯特拉(Dijkstra)算法

定义

Dijkstra(迪杰斯特拉)算法是计算单源最短路径算法,用于计算一个结点到其他所有结点的最短路径。该算法以源点为起始点,不断更新其他点到已经确定距离结点的距离,选取距离最小的结点加入S集合,直到S集合存放有所有的结点

算法思想

现在一张图中有n个结点,有两个集合,S集合和V集合。S集合表示已经选取的结点,V集合表示还没有选取的结点

  1. 确定一个源点,放入S集合,剩下n-1个结点放入V集合
  2. 计算从源点经过S集合中的各点到达V集合中各点的距离,与之前确定的距离进行比较,如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离,并选取距离最小的结点放入S集合,直到V集合为空(在计算过程中直接计算从最后加入S集合的点到源点的距离+最后加入S集合的点到V集合中各点的距离,表示从源点到达V集合中各点要经过最后加入S集合中的点,与原来的距离比较即可

还是很抽象,举个例子

示例

我们以有向图为例
在这里插入图片描述
比如我们选取结点1为源点,利用Dijkstra算法计算源点到其他各点的距离
那么现在S=[1],V=[2,3,4,5]
计算此时从源点出发经过最后加入S集合中的点(源点)到达V集合中各点的距离
经过S集合中的各点到达2的距离:10
经过S集合中的各点到达3的距离:infinity
经过S集合中的各点到达4的距离:30
经过S集合中的各点到达5的距离:100
此时选取距离最小的结点2加入S集合,即S=[1,2],V=[3,4,5]

计算此时从源点出发经过最后加入S集合中的点(结点2)到达V集合中各点的距离
如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离
源点到结点2的距离+结点2到V集合中结点3的距离:10+50=60,小于infinity,更新
源点到结点2的距离+结点2到V集合中结点4的距离:10+infinity,不小于原来的30,不更新
源点到结点2的距离+结点2到V集合中结点5的距离:10+infinity,不小于原来的100,不更新
故:
3:60
4:30
5:100
此时选取距离最小的结点4加入S集合,即S=[1,2,4],V=[3,5]

计算此时从源点出发经过最后加入S集合中的点(结点4)到达V集合中各点的距离,如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离
源点到结点4的距离+结点4到V集合中结点3的距离:30+20=50,小于原来的60,更新
源点到结点4的距离+结点4到V集合中结点5的距离:30+60=90,小于原来的100,更新
故:
3:50
5:90
此时选取距离最小的结点3加入S集合,即S=[1,2,4,3],V=[5]

计算此时从源点出发经过最后加入S集合中的点(结点3)到达V集合中各点的距离
源点到结点3的距离+结点3到V集合中结点5的距离:50+10=60,小于原来的90,更新
故:
5:60
此时选取距离最小的结点5加入S集合,即S=[1,2,4,3,5],V=[ ]

因为在生成最短路径的过程中,从源点到达V集合中各点时都要经过S集合中的各点,故生成的最短路径如下:
在这里插入图片描述
在加入顶点的过程中我们得到了单源最短路径,从源点到2,3,4,5的距离分别为:10,50,30,60

代码

#include<iostream>

using namespace std;

const int inf = 999; 

void Dijkstra(int n,int source,int dist[],int prev[],int c[6][6])
{
	bool s[inf];//用于表示点是否在s集合里 
	for(int i=1; i<=n; i++)
	{
		dist[i] = c[source][i];
		s[i] = false;//初始化的时候,除了source外所有的点都不在 s集合 
		if(dist[i] == inf) prev[i] = 0;
		else prev[i] = source;//如果相邻,前驱点是源点 
	} 
	dist[source] = 0;
	s[source] = true;
	
	for(int i=2; i<=n; i++)//没有用到i的值,只是控制循环次数,表示要找n-1个点 
	{
		int minDist = inf;
		int u = source;//u记录下距离source最近的点
		
		for(int j=1; j<=n; j++)	//从v-s集合中找点 
		{
			//j点不在s集合中,且到source的距离小于上一个点到source的距离,就用u记录下该点 
			if(!s[j] && dist[j]<minDist)
			{
				u = j;
				minDist = dist[u];
			}
		}
		
		s[u] = true;//将u点加入s集合
		
		for(int j=1; j<=n; j++)//当s集合加入新点的时候需要更新dist[]和prev[] 
		{
			if(!s[j] && c[u][j]<inf)//j点不在s集合中,且 新点与 j点相邻 
			{
				int newDist = dist[u] + c[u][j];//新点到source的距离 + 新点到 j点的距离
				if( newDist < dist[j] ) 
				{
				    dist[j] = newDist;
					prev[j] = u;//新点成了j的前驱点,表示此时从source到j点经过u距离最短 
				}
			}
		} 
	} 
}

int main()
{
	/*
	n个点
	dist[i]表示从source点到 i点的最短距离
	k=previous[i]表示经过 k点到达 i点才是最短路径 
	c[][]是临接矩阵 
	*/ 
int c[6][6]={	
				{inf,inf,inf,inf,inf,inf},
			    {inf,0,10,inf,30,100},
			 	{inf,inf,0,50,inf,inf},
			 	{inf,inf,inf,0,inf,10},
				{inf,inf,inf,20,0,60},
				{inf,inf,inf,inf,inf,0}
			};
	int dist[6];
	int prev[6];
	int source = 1;
	int n = 5;
	Dijkstra(n,source,dist,prev,c);	
	cout<<"从源点到各点的最短路径是(距离为0表示源点):";
	for(int i=1; i<=n; i++)
	cout<<dist[i]<<" ";
	cout<<endl;
	cout<<"下面输出从源点到各点的反向路径"<<endl; 
	for(int i=1; i<=n; i++)
	{
		if(i != source) 
		{
			int j = i;
			cout<<"从源点到"<<j<<"点的最短路径为:"<<endl;
			int k = j;
			while(k != source)
			{
				if( j!=source )
					cout<<j<<"  <-  ";
				else 	
					cout<<j;
				k = j;
				j = prev[j];
			}
			cout<<endl;
		}
	}
	return 0;
}

运行结果

在这里插入图片描述

发布了15 篇原创文章 · 获赞 9 · 访问量 1163

猜你喜欢

转载自blog.csdn.net/qq_42500831/article/details/89608104