图——最短路径

一、Dijkstra算法(单源最短路径算法)

狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。

要计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法。在无向图中,每条边都是一个环。狄克斯特拉算法只适用有向无环(directed acyclicgraph,DAG),但是无向边也可以当成两条指向相反的有向边。Dijkstra 算法可以很好地解决无负权图的最短路径问题。

狄克斯特拉算法背后的关键理念:找出图中最便宜的节点,并确保没有到该节点的更便宜的路径!

邻接矩阵和邻接表实现的代码

#include<iostream>
#include<algorithm>
#include<vector>
const int MAXV = 1000;//最大顶点数
const int INF = 1000000000;//设INF为一个很大的数 
using namespace std;

//领接矩阵版
//适用于点数不大(例如V小于1000的情况) 
//初始化数据 
int n;//图的顶点数
int G[MAXV][MAXV];//图的邻接矩阵,MAXV为最大顶点数
int d[MAXV]; //源点到达各点的最短路径长度 
int vis[MAXV] = {false};//vis[i]==true表示已访问,初值均为false。 
void Dijkstra(int s){
	//u是中介点(源点到待访问节点的最短距离对应的结点标号)
	//MIN保存从源点到待访问节点的最短距离 
	fill(d,d+MAXV,INF);
	d[s] = 0;	//除源点自身处的最短距离为0之外,源点到其余点的距离首先都是INF。 
	for(int i = 0;i<n;i++){
		int u = -1,MIN = INF;  
		for(int j=0;j<n;j++){
			//找到未访问的顶点中,d[]最小的(即从源点到该点有路径,也有距离,但此时不确定是最短的) 
			if(vis[j]==false && d[j]<MIN){
				u = j;	//u表示源点到未访问结点的最短距离对应的结点的标号 
				MIN = d[j];
			}
		} 
		//若找不到最短路径,u==-1,则中断返回函数
		if(u==-1) return;
		//将u作为中介点(开放节点),探索源点经过u到达其余结点v的路径距离是否比d[v]小,是,则更新
		vis[u] = true;//标记u为已访问 
		for(int v=0;v<n;i++){
			//如果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];
			}
		} 
	} 
}


//邻接表版 
struct Node{
	int v;//v为边的目标顶点
	int dis;//dis为边权 
};
//图G,Adj[u]存放从顶点u出发可以到达的所有顶点 
vector<Node> Adj[MAXV];//MAXV在领接矩阵中已经定义
//int n; //实际顶点数(若与邻接矩阵表示的算法分开写,则注释需要删除,下同)
//int d[MAXV]; //源点到达各点的最短路径长度
//bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问

void Dijkstra_(int s){
	fill(d,d+MAXV,INF); 
	d[s] = 0;
	for(int i=0;i<n;i++){
		int MIN = INF;
		int u = -1;
		for(int j = 0;j<n;j++){
			if(vis[j] == false && d[j]<MIN){
				u = j;
				MIN = d[j];
			}	
		}
		//找不到小于INF的d[u],说明剩下的顶点和起点s不连通
		if(u == -1) return;
		vis[u] == true; //标记u为已访问
		//只有下面这个for与邻接矩阵的写法不同 
		for(int j=0;j<Adj[u].size();j++){
			int v = Adj[u][j].v; //通过邻接表直接获得u能到达的顶点v
			if(vis[v] == false && d[u]+Adj[u][j].dis < d[v]){
				//如果v未访问&&以u为中介点可以使d[v]更优
				d[v] = d[u] + Adj[u][j].dis; //优化d[v]	
			}
		}
	}
}

 

用基于邻接矩阵的Dijkstra算法和主函数进行测试

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXV = 1000;   //邻接矩阵适合的最大顶点数 
const int INF = 1000000000;//设置INF为一个很大的数

int n,m,s,G[MAXV][MAXV]; //n为顶点数,m为边数,s为起点
int d[MAXV];	//源点到达各点的最短路径长度
bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问

void Dijkstra(int 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;//标记顶点u为已访问 
		for(int v = 0;v<n;v++){
			if(vis[v] == false && G[u][v]!=INF && d[u]+G[u][v]<d[v]){
				d[v] = d[u]+G[u][v]; 
			}
		}
	} 
}


int main(){
	int u,v,w;
	cin>>n>>m>>s;//顶点个数,边数,起点编号
	fill(G[0],G[0]+MAXV*MAXV,INF);
	for(int i=0;i<m;i++){
		cin>>u>>v>>w;
		G[u][v] = w;
	} 
	Dijkstra(s);	//单源最短路径算法入口 
	for(int i = 0;i<n;i++){
		if(i>0) cout<<" ";
		cout<<d[i];
	}
	return 0; 
}

二、Floyd算法(全源最短路径算法)

//Floyd算法(全员最短路径算法)
//时间复杂度是O(n3),决定了顶点数n的限制约在200以内,因此使用邻接矩阵实现该算法 
/*
算法思想: 
枚举顶点k∈[1,n] 
	以顶点k作为中介点,枚举所有顶点对i和j(i∈[1,n],j∈[1,n])
		如果dis[i][k] + dis[k][j] <dis[i][j]成立
			赋值dis[i][j] = dis[i][k]+dis[k][j] 
*/
//实现代码
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXV = 200;//MAXV为最大顶点数,在该算法中,允许200个顶点。 
const int INF = 1000000000;
int n,m;//n为顶点数,m为边数
int dis[MAXV][MAXV];//dis[i][j]表示顶点i和1顶点

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);
	cin>>n>>m;
	for(int i=0;i<n;i++){
		dis[i][i] = 0;//顶点i到顶点i的距离初始化为0 
	}
	for(int i=0;i<m;i++){
		cin>>u>>v>>w;
		dis[u][v] = w;//以有向图为例进行输入 
	}
	Floyd();//Floyd算法入口
	for(int i=0;i<n;i++){//输出dis数组
		for(int j=0;j<n;j++){
			cout<<dis[i][j]<<" ";
		} 
		cout<<endl;
	} 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值