数据结构之——最短路径(Dijkstra算法和Floyd算法)

最短路径是图论中一个很经典的问题:给定图G(V,E),求一条从起点到终点的路径,使的这条路径上经过的所有边的边权之和最小。

1.Dijkstra算法

Dijkstra算法(迪杰斯特拉)用来解决单源最短路径问题,即给定图G和起点s,通过算法得到s到达其他顶点的最短距离。Dijkstra的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S,之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离,这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。

Dijsktra算法的策略是:
设置集合S存放已被访问的顶点(即已攻占的城市),然后执行n次下面的两个步骤(n为顶点个数):
(1)每次从集合V-S(即未攻占的城市)中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S(即令其已被攻占)。
(2)之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。

Dijkstra算法的具体实现:
(1)集合S可以用一个bool数组vis[]来实现,即当vis[i]==true时表示顶点Vi已被访问,当vis[i]==false时表示顶点Vi未被访问。
(2)令int型数组d[]表示起点s到达顶点Vi的最短距离,初始时除了起点s的d[s]赋为0,其余顶点都赋为一个很大的数,来表示inf,即不可达。

伪代码如下:

//G为图,一般设为全局变量:数组d为源点到达各点的最短路径长度,s为起点
Dijkstra(G, d[], s){
	初始化;
	for(循环n次){
		u = 使d[u]最小的还未被访问的顶点的标号;
		记u已被访问;
		for(从u出发能到达的所有顶点v){
			if(v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优{
			优化d[v]; 
			)
		}
		 
	}


} 

邻接矩阵版Dijkstra算法:

constint MAXV=1000;   //最大顶点数
constint INF=1000000000;  //设INF为一个很大的数

int n, G[MAXV][MAXV];   //设n为顶点数,MAXV为最大顶点数
int d[MAXV] ;  //起点到达各点的最短路径长度 
bool vis[MAXV] = {false};  //标记数组,初值为false,表示未被访问

void Dijkstra(int s){// s为起点
	 fill(d, d+MAXV, INF);   //fill函数将整个d数组赋为inf
	 d[s] = 0;  //起点s到自身的距离为0
	 
	 for(int =0; i<n; i++){     //循环n次 
	 
	 	int u=-1, MIN = INF;     //u使d[u]最小,MIN存放该最小的d[u]
		 for(int j=0; j<n; j++){   //找到未访问的顶点中d[]的最小 
		 		 if(vis[j] == false && d[j]<MIN){
		 		 	MIN = d[j];
		 			u = j;
		 			} 
		   } 
		   
		   //找不到小于INF的d[u],说明剩下的顶点和起点s不连通
		   if(u == -1)  return;
		   vis[u] = true;   //标记u已访问
		   
		   for(int v=0; v<n; v++){
		   	//如果v未访问 && u能到达v &&以u为中介点可以使d[v]更优
			   if(vis[v] == false && G[u][v] != INF&& d[u]+G[u][v]<d[v]){
			   	d[v] = d[u] + G[u][v];   //优化d[v] 
			   } 
		   } 
	 } 
} 

邻接表版Dijkstra算法:

vector<Node> Adj[MAXV];  //Adj[u]存放从顶点u出发可以到达的所有顶点
int n;
int d[MAXV];
bool vis[MAXV] ={false};

void Dijkstra(int s){   //s为起点 
	 
	 fill(d, d+MAXV, INF);
	 d[s] = 0;
	 for(int i=0; i<n; i++){
	 	int u =-1, MIN = INF;
	 	for(int j=0; j<n; j++){
	 		if(vis[j] == false && d[j] < MIN){
	 			u = j;
	 			MIN = d[j];
			 }
		 }
		 if(u == -1)  return;
		 vis[u] = true;
		 for(int j=0; j<Adj[u].size; j++){
		 	int v = Adj[u][j].v;
		 	if(vis[v] == false && d[u]+Adj[u][j].dis < d[v] ){
		 		d[v] = d[u] + Adj[u][j].dis;  //优化 
			 }
		 }
	 }
}

2.Floyd算法

Floyd算法用来解决全源最短路径,即对给定的图G(V,E),求任意两点u,v之间的最短路径长度,时间复杂度O(n3),由于n3的复杂度,由于n3的复杂度决定了顶点数n的限制约在200以内,因此使用邻接矩阵Floyd算法是非常合适且方便的。

Floyd算法基于这样一个事实:如果存在顶点k,使得以k作为中介点时顶点i和顶点j的当前最短距离缩短,则使用顶点k作为顶点i和顶点j的中介点,即当dis[i][k] + dis[k][j] < dis[i][j]时,令dis[i][j] = dis[i][k] + dis[k][j] (其中dis[i][j]表示从顶点i到顶点j的最短距离)。Floyd算法的流程如下:


枚举顶点1<= k <=n
	以顶点k作为中介点,枚举所有顶点对i和j
		如果dis[i][k] + dis[k][j] <dis[i][j]成立
			赋值dis[i][j] = dis[i][k] + dis[k][j] 

Floyd算法代码如下:


#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 1000000000;  
const int MAXV = 200;  //MAXV为最大顶点数 //MAXV为最大顶点数 
const int n , m;     //  n为顶点数,m为边数
int dis[MAXV][MAXV];   //dis[i][j]表示顶点i和顶点j的最短距离

void Floyd(){
	for(int k =0; k<n; k++){
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				if(dis[i][k]!=INF && dis[k][j]!=INF && dis[i][k] + dis[k][j] < dis[i][j]){
					dis[i][j] = dis[i][k] + dis[k][j];  //优化路径 
				}
			}
		}
	}
} 

int main(){
	int u, v, w;
	fill(dis[0],  dis[0]+ MAXV*MAXV, INF);  //dis 赋处值
	sacnf("%d%d", &n, &m);  //顶点数n、边数m
	
	for(int i=0; i<m; i++){
		dis[i][i] = 0;   //顶点i到顶点i的距离初始化为0 
	} 
	
	for(int i=0; i<n; i++){
		scanf("%d%d%d", &u, &v, &w);
		dis[u][v] = w;  //以有向图为例进行输入 
	}
	
	Floyd();  //算法入口
	
	 for(int i =0; i<n; i++){
	 	for(int j=0; j<n; j++){
	 		printf("%d ", dis[i][j]);
		 }
		 printf("\n")
	 }
	 return 0;
}

猜你喜欢

转载自blog.csdn.net/xiaoyong5854/article/details/106554919