图论(三)——单源最短路径之Bellman-Ford算法和SPFA算法

单源最短路径问题,即给定一张有向图(其实无向图也可以看作是有向图,即将无向的一条边看成是两条有向的边),设1号节点为起点,求其到i号节点的最短距离

先来给出一个具体的题目,然后看看这两种算法是怎么写出代码来解决的
【使用Dijkstra算法的解决方案:图论(二)——单源最短路径之Dijkstra算法
HDU最短路

最短路题目1
最短路题目2

Bellman-Ford算法

这个算法很好理解,它的操作的主要思想如下:


设一个有numNode个元素的数组dis,其中dis[i]表示从1号节点到i号节点的最短距离(在操作过程中不断更新),将dis[1]初始化为0,将其他元素初始化为无穷大


  1. 扫描所有的边 (from , to , weight) ,如果有dis[from]+weight<dis[to],则将dis[to]更新为dis[from]+weight( 即 dis[to]=min(dis[to],dis[from]+weight) )
  2. 重复上述操作,直到没有更新操作为止

这样看来这个算法非常好理解,每次考虑到所有的边,在循环的过程中逐渐确定了dis[i]的每一个值,可见算法的时间复杂度为O(numNode*numEdge)

那么使用Bellman-Ford算法解决这个问题的代码如下:

#include<cstdio>
#include<cstring>
#include<climits>
using namespace std;
const int lenNode=110;
const int lenEdge=20010;  //注意这是无向图 

int dis[lenNode],m,n;
struct Edge{
	int from,to,weight;
}edge[lenEdge];
int cnt;

void init(){
	memset(dis,INT_MAX,sizeof(dis));
	dis[1]=0;
	
	cnt=0;
} 

void addEdge(int u,int v,int w){
	edge[cnt].from=u;
	edge[cnt].to=v;
	edge[cnt++].weight=w;
}

int mark;
void Bellman_Ford(){
	mark=0;
	for(int i=0;i<cnt;i++){
		if(dis[edge[i].to]>dis[edge[i].weight]+edge[i].weight){
			dis[edge[i].to]=dis[edge[i].weight]+edge[i].weight;
			mark=1;
		}
	}
	
	if(mark){
		Bell_Ford();
	}
}

int main(){
	int a,b,c;
	while(~scanf("%d%d",&n,&m)&&n){
		init();
		
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&a,&b,&c);
			addEdge(a,b,c);
			addEdge(b,a,c);  //注意这是无向图 
		}
		
		Bellman_Ford();
		printf("%d\n",dis[n]);
	}
	
	return 0;
}

SPFA算法

其实SPFA算法就是对Bellman-Ford算法的一种优化,SPFA算法又称为【队列优化的Bellman-Ford算法】,其主要思想如下:


  1. 建立一个队列,最初在队列中加入起点1,设一个数组vis,这个数组用来记录节点是否已经处于队列之中
  2. 取出队头节点i,扫描他的所有出边(i , to , weight),如果dis[i]+weight<dis[to],那么则用dis[i]+weight更新dis[to],如果节点to未在队列中(vis[to]==0),则将节点i加入队列,并将节点to标记(vis[to]=1)
  3. 重复步骤二,直到队列为空

那么使用SPFA算法解决这个问题的代码如下:

#include<cstdio>
#include<cstring>
#include<climits>
#include<vector>
#include<queue>
using namespace std;
const int lenNode=110;
const int lenEdge=20010;  //注意这是无向图 

int dis[lenNode],vis[lenNode],n,m;
struct Edge{
	int from,to,weight;
	int nxt;
}edge[lenEdge];
int cnt,head[lenNode];

void init(){
	memset(dis,INT_MAX,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[1]=0;
	
	cnt=0;
	memset(head,0,sizeof(head));
	for(int i=0;i<lenEdge;i++){
		edge[i].nxt=0;
	}
}

void addEdge(int u,int v,int w){
	edge[cnt].from=u;
	edge[cnt].to=v;
	edge[cnt].weight=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}

int point;
void SPFA(){
	queue<int>node;
	vis[1]=1;
	node.push(1);
	while(!node.empty()){
		point=node.front();
		q.pop();
		vis[point]=0;
		
		for(int i=head[point];i;i=edge[i].nxt){
			if(dis[edge[i].to]>dis[edge[i].from]+edge[i].weight){
				dis[edge[i].to]=dis[edge[i].from]+edge[i].weight;
				if(!vis[edge[i].to]){
					node.push(edge[i].to);
					vis[edge[i].to]=1;
				}
			}
		}
	}
}

int main(){
	int a,b,c;
	while(~scanf("%d%d",&n,&m)&&n){
		init();
		
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&a,&b,&c);
			addEdge(a,b,c);
			addEdge(b,a,c);  //注意这是无向图 
		}
		
		SPFA();
		printf("%d\n",dis[n]);
	}
	
	return 0;
}
发布了21 篇原创文章 · 获赞 6 · 访问量 647
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览