Dijkstra算法(有权图单源最短路径)

  从一个源点到其他各顶点的最短路径问题称为“单源最短路径问题”。

  • 最短路径的最优子结构性质

    • 该性质描述为:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。
    • 假设P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P’(k,s),那么P’(i,j)=P(i,k)+P’(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。

  • Dijkstra算法

    • 定义dist[W] = S到W的最短距离;
      dist[S] = 0;
      path[W] = S到W路径上经过的顶点(即W前一个父顶点)。
    • 由上述性质可知,如果存在一条从i到j的最短路径(Vi…Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。 譬如对于源顶点s,
      • 选择其直接相邻的顶点中长度最短的顶点V,此时将V收录到集合S ={源点s + 已经确定了最短路径的顶点V}中;
      • V进入集合S后,将有可能影响V一圈邻接点W的dist值,dist[W] = min{dist[W], dist[V] + <V, W>的权重};
      • 最短路径找到,停止。

  • 伪代码

    void Dijkstra(Vertex V)
    {
    	while (1) {
    		V = 未收录顶点中dist最小者;
    		if ( 这样的V不存在)
    			break;
    		collected[V] = true;
    		for ( V的每个邻接点W )
    			if (collected[W] == false && dist[V] + E<V, W> < dist[W] ){
    				dist[W] = dist[V] + E<V, W>;
    				path[W] = V;
    			}
    	}
    }
    

  • 算法复杂度

    • FinMinDist可以用小顶堆实现。
      在这里插入图片描述

  • 参考代码

/* 
	Name: Dijkstra算法 
	Copyright: 
	Author: xuuyann 
	Date: 07/11/18 14:31
	Description: 
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <stack>
using namespace std;

//图的邻接矩阵表示法
#define MaxVertexNum 100 //最大顶点数设为100 
#define INFINITY 65535 //无穷设为双字节无符号整数的最大值65535 
typedef int Vertex;//用顶点下标表示顶点,为整型 
typedef int WeightType;//边的权值设为整型 
typedef char DataType;//顶点存储的数据类型设为字符型 
//图结点的定义
typedef struct GNode *PtrToGNode;
struct GNode {
	int Nv;//顶点数 
	int Ne;//边数 
	WeightType G[MaxVertexNum][MaxVertexNum];//邻接矩阵,表示顶点间的相邻关系 
	DataType Data[MaxVertexNum];//存顶点的数据
	//注意:如果顶点无数据,此时Data[]可以不用出现 
}; 
typedef PtrToGNode MGraph;//用邻接矩阵存储图的类型
//边的定义
typedef struct ENode *PtrToENode;
struct ENode {
	int V1, V2;//边的两个顶点 
	WeightType Weight;//边的权重 
}; 
typedef PtrToENode Edge;

MGraph CreateGraph(int VertexNum)
//图的创建,并初始化一个具有VertexNum个顶点且Edge为0的空图
{
	MGraph Graph = (MGraph)malloc(sizeof(struct GNode ));
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	for (int i = 0; i < Graph->Nv ; i++){
		for (int j = 0; j < Graph->Nv ; j++ )
			Graph->G[i][j] = INFINITY;
	}
	
	return Graph;
 } 
 
void InsertEdge(MGraph Graph, Edge E)
//插入边 
{
	Graph->G[E->V1 ][E->V2 ] = E->Weight ;
	//若是无向图,则还要插入边<V2, V1>
	//Graph->G[E->V2 ][E->V1 ] = E->Weight ; 
}

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 -1; 
}

bool Dijkstra(MGraph Graph, int *dist, int *path, Vertex S)
//dist[V]=S到V的最短距离,dist[S]=0,path[V]=S到V路上经过的父顶点 
{
	int collected[MaxVertexNum];
	Vertex V, W;
	//初始化:此处默认邻接矩阵中不存在的边用INFINITY 
	for (V = 0; V < Graph->Nv ; V++){
		dist[V] = Graph->G[S][V];
		if (dist[V] < INFINITY)
			path[V] = S;//将S周围一圈的邻接点全赋予父顶点S 
		else
			path[V] = -1; 
		collected[V] = false;
	}
	//先将源点S收入集合中
	dist[S] = 0;//path[S] = -1
	collected[S] = true;
	while (1){
		//V=未被收录顶点中dist最小者
		V = FindMinDist(Graph, dist, collected); 
		if (V == -1)
			break;//算法结束 
		collected[V] = true;
		for (W = 0; W < Graph->Nv ; W++){//对于图中每一个顶点W 
			if (collected[W] == false && Graph->G[V][W] < INFINITY){
			//若W是V的邻接点并且W未被收录
				if (Graph->G[V][W] < 0)
					return false;//若有负值圈,则不能正确解决,返回错误标记 
				if (dist[V] + Graph->G[V][W] < dist[W]){
				//该情况为,V进入集合S后可能影响另外一个W的dist,且只影响V周围一圈邻接点的dist值 
					dist[W] = dist[V] + Graph->G[V][W];//更新dist[W] 
					path[W] = V;//更新S到W的路径,即增加W的父顶点V 
				} 
			}
		}
	}
	return true;//算法执行完毕,返回正确标记	
}

void PrintPath(int *path, Vertex W, Vertex S)
//打印最短路径经过的所有顶点,此处打印0(path[0]=-1)到每个顶点的最短路径 
//先逆序push,再顺序pop 
{
	stack<int> s;
	s.push(W);//path[W] = V,path[S] = -1
	while (W != S){
		W = path[W];
		s.push(W) ;
	}//W = S时跳出循环,即到达源点 
	while (!s.empty() ){
		if (s.size() == 1 )
			printf("%d", s.top() );
		else
			printf("%d ", s.top() );
		s.pop() ;
	} 
}

int main()
{
	MGraph Graph;
	int VertexNum, EdgeNum;
	scanf("%d %d", &VertexNum, &EdgeNum);
	int *dist = (int *)malloc(sizeof (int) * VertexNum);//初始化dist 
	int *path = (int *)malloc(sizeof (int) * VertexNum);//初始化path 
	Graph = CreateGraph(VertexNum);
	Graph->Ne = EdgeNum;
	Edge E = (Edge)malloc(sizeof(struct ENode ));
	for(int i = 0; i < Graph->Ne ; i++){
		scanf("%d %d %d", &E->V1 , &E->V2 , &E->Weight );
		InsertEdge(Graph, E);
	}//至此,图创建完成
	//下面进入Dijkstra算法部分
	int S = 0;//定义源点S,Graph->G[0][0]=INFINITY
	Dijkstra(Graph, dist, path, S);
	//PrintPath(path);
	for (int i = 0; i < VertexNum; i++){
		if (i != S){
			PrintPath(path, i, S);
			printf(" %d\n", dist[i]);
		}
	}
	
	return 0;
	 
}

  • 测试数据:
    在这里插入图片描述

  • 测试结果:
    在这里插入图片描述

Reference

猜你喜欢

转载自blog.csdn.net/qq_26565435/article/details/83824012
今日推荐