图的最短路径问题

图的最短路径问题

最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
单源点最短路径是指:给定一个出发点(单源点)和一个有向网G=(V,E),求出源点到其它各顶点之间的最短路径。
迪杰斯特拉(Dijkstra)在做了大量观察后,首先提出了按路径长度递增序产生各顶点的最短路径算法,我们称之为迪杰斯特拉算法
算法的基本思想是: 把图中顶点集合分成两组,第一组为集合S,存放已求出其最短路径的顶点,第二组为尚未确定最短路径的顶点集合是V-S(用U表示),其中V为网中所有顶点集合。按最短路径长度递增的顺序逐个把U中的顶点加到S中,直到S中包含全部顶点,而U为空。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
(1)初始时,S只包含源点,S={v},v的距离为0。U包含除v外的其他顶点,U中顶点的距离为顶点的权或∞ 。
(2)从U中选取一个距离最小的顶点k,把k加入到S中
(3)以k 作为新考虑的中间点,修改U中各顶点的距离。
(4)重复步骤(2)、(3)直到所有顶点都包含在S中。
时间复杂度:O(V^3)

图的最短路径问题_第1张图片
图的最短路径问题_第2张图片

//有权图的Dijikstra(遍历整个数组寻找最小路径顶点)
bool Dijikstra(int vertex){
	//根据初始结点初始化距离数组与路径数组 
	for(int i = 0 ; i < this->Nv+1 ; i++){
		//在构造函数里dist已经全部初始化为MAX
		//G存在边时为权重,没有边时为MAX 
		this->dist[i] = this->G[vertex][i];
		if(this->dist[i] < MAX){
			this->path[i] = vertex;
		}
	}
	this->dist[vertex] = 0;		//初始结点的距离为0
	this->collected[vertex] = 1;	//初始结点标记为已收录 
	while(1){
		//V是未被收录定点中dist最小者 
		int V = this->FindMinVertex(); 
		if(V == -1){//未找到这样的V则跳出循环 
			break;
		} 
		this->collected[V] = 1;//标记为已经被收录 
		//遍历图中每个顶点 
		for(int w = 1 ; w < this->Nv+1 ; w++){
			//若w是V的邻接点且未被收录 
			if(this->collected[w] == 0 && this->G[V][w] < MAX){
				if(this->G[V][w] < 0){//存在负边时 
					return false;	//结束算法 
				}
				//若收录V使得dist[w]变小 
				if(this->dist[V] + this->G[V][w] < this->dist[w]){
					//更新dist[w] 
					this->dist[w] = this->dist[V] = this->G[V][w]; 
					this->path[w] = V;//更新路径 
				} 
			} 
		} 
	}
	return true;
}

最短路径算法中的Floyd算法,这是针对多源最短路径的一个经典算法。
算法思想:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)。
从任意节点i到任意节点j的最短路径不外乎2种可能,一是直接从i到j,二是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
时间复杂度:O(V^3)
算法过程:
1)首先把初始化距离dist数组为图的邻接矩阵,路径数组path初始化为-1。其中对于邻接矩阵中的数首先初始化为正无穷,如果两个顶点存在边则初始化为权重。
2)对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是就更新它。
状态转移方程为
如果 dist[i][k]+dist[k][j] < dist[i][j]
则 dist[i][j] = dist[i][k]+dist[k][j]

bool Floyd(){
	for(int k = 1 ; k < this->Nv+1 ; k++){	//k代表中间顶点 
		for(int i = 1  ; i < this->Nv+1 ; i++){//i代表起始顶点 
			for(int j = 1 ; j < this->Nv+1 ; j++){//j代表终点 
				if(this->dist[i][k] + this->dist[k][j] < this->dist[i][j]){
					this->dist[i][j] = this->dist[i][k] + this->dist[k][j];
					if(i == j && this->dist[i][j] < 0){//发现了负值圈 
						return false;
					}
					this->path[i][j] = k;
				}					
			}
		}
	}
	return true; 
}

你可能感兴趣的:(数据结构,图结构)