单源最短路径(Dijkstra算法及优化)

Dijkstra算法用于计算一个有权图中某一点到图中任意一点的最短路径
使用前提 :该图没有负权边
复杂度 :O(V^2) V为图中的顶点个数,优化后是Elog(E)
算法思想
使用一个cost数组,cost[i]表示从起点到编号为i这个点的最短路径花费
使用一个visited数组,用来表示cost[i]是否计算完成
从起始点开始,更新与它直接相邻的节点的cost数组,然后在未计算完成的剩余的点找到花费最少的点,这个点的cost数组代表的就是从起点到这一个点的最短路径花费,然后以这个点继续更新与之相邻的点,直至找到所有的点
实现过程
根据输入的数据建图,然后从起始点开始,经过n-1次循环,在每一轮循环中,更新与该点相邻边的dist值。更新完后找到花费最小的点,更新cost数组,然后下一次循环以该点继续重复循环。
未经优化的代码

#include <iostream>
#include <vector>
#include <algorithm>
#define maxn 10
using namespace std;

int dist[maxn],vis[maxn] = {0}; 

struct node{    		
	int value;
	int num;
};

vector<node> g[maxn]; 

int main()
{
	int n,m;
	cin >> n >> m;
	for( int i = 0 ; i < n ; i++ )
	{
		dist[i] = 0x7fffffff;
	}
	for( int i = 0 ; i < m ; i++ )      //输入数据表示建立一张图
	{
		int x,y,v;
		cin >> x >> y >> v;
		node t;
		t.value = v;
		t.num = y;
		g[x].push_back(t);
		t.num = x;
		g[y].push_back(t); 
	}
	int begin = 0,minx = 0;
	dist[begin] = 0;
	for( int i = 0 ; i < n-1 ; i++ )     //一共要遍历n-1次,找到n-1个点与起始点的最短花费
	{
		vis[begin] = 1;
		for(int j = 0 ; j < g[begin].size() ; j++ )    //遍历这一个点所有与它相邻的点
		{
			int t = g[begin][j].num;
			dist[t] = min(dist[t],dist[begin] + g[begin][j].value);     //更新它的数据
		}
		minx = 0x7fffffff;
		for( int j = 0 ; j < n ; j++ )     //在n个点中找到花费最少且没有计算完成的点
		{
			if( !vis[j] && minx > dist[j] )    
			{
				begin = j;            //记录这个点的编号
				minx = dist[j];
			}
		}
	}
	for( int i = 0 ; i < n ; i++ )
	{
		cout << dist[i] << endl;
	}
    return 0;
}

/*
5 8
0 1 5
0 2 2
0 3 6
2 1 1
2 3 3
2 4 5
1 4 1
3 4 2
*/

显然上述算法在查找值最小的点时用了O(V)的时间,导致整体的复杂度变成了O(V^2);其实我们可以用一个优先队列来优化,用log(V)的时间找到权值最小的点
实现过程 :先建图,把起点入队,然后松弛队首元素,把vis数组置1,松弛成功后,更新dist数组,把节点入队(注意入队的节点value值等于dist的值),直到队列为空
优化代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstring>
#define maxn 100005
using namespace std;

typedef long long ll;

ll dist[maxn],vis[maxn]; 

struct node{    		
	int num;
	ll val;        //对于在个g[i]的0vector里的node,value是存放i到这个点需要的权值。在优先队列的node,value代表的是从起点到这个点的值
	node(int a,ll b)
	{
		num = a;
		val = b;
	}
	bool operator<(const node&n) const
	{
		return val > n.val;
	}
};
vector<node> g[maxn]; 

void dij(int begin,int n)
{
	priority_queue<node> q;
	memset(vis,0,sizeof(vis));
	for( int i = 1 ; i <= n ; i++ )
	{
		dist[i] = 1e18;           //设为无穷大
	}
	dist[begin] = 0;    
	q.push(node(begin,0));     //把开始节点入队
	while( !q.empty() )      //while到队列为空
	{
		int x = q.top().num;
		q.pop();
		if( vis[x] ) continue;          //如果这个点已经被访问过了,直接跳过
		vis[x] = 1;
		for( int i = 0 ; i < g[x].size() ; i++ )
		{
			node t = g[x][i];
			if( !vis[t.num] && dist[t.num] > dist[x] + t.val )       //更新花费
			{
				dist[t.num] = dist[x] + t.val;
				t.val = dist[t.num];        //这一步一定要更新入队的节点的value值
				q.push(t); 
			}
		}
	}
}

int main()
{
	
	int n,m,begin;
	cin >> n >> m >> begin; 
	for( int i = 0 ; i < m ; i++ )   
	{
		int x,y,v;
		cin >> x >> y >> v;
		g[x].push_back(node(y,v));     //建图
	}
	dij(1,n);
	for( int i = 1 ; i <= n ; i++ )
	{
		if( i!=n ) cout << dist[i] << " " ;
		else cout << dist[i] << endl;
	}
    return 0;
}

在这个代码中,我们跑了n-1个点,并且遍历了它们的所有边,在最坏情况下,每一次的遍历边都会往队列里push进节点。所以复杂度是ElogE。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值