Dijkstra法解单源最短路径问题(附手写堆代码)

Dijkstra法解单源最短路径问题(附手写堆代码)

原题来自洛谷【模板】单源最短路径(标准版)

题目描述

给定一个 n n n 个点, m m m 条有向边的带非负权图,请你计算从 s s s 出发,到每个点的距离。

数据保证你能从 s s s 出发到任意点。

输入格式

第一行为三个正整数 n , m , s n,m,s n,m,s 。 第二行起 m m m 行,每行三个非负整数 ui,vi,wi, 表示从 ui到 vi 有一条权值为 wi 的有向边。

输出格式

输出一行 n n n 个空格分隔的非负整数,表示 s s s 到每个点的最短距离。

输入输出样例
//输入
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
    
//输出
0 2 4 3

Dijkstra算法

简要原理:Dijkstra算法的本质思想为贪心,同时只适用于不含负权边的图,原理为每次找到一个dis最小的点,然后用其去更新周围的点,直到所有的点都被更新即结束。

主要过程:①首先初始化dis[s]为0 (s为出发点),其余点的dis值设为无穷大;②由已给权值更新s到其余点的dis值;③找出目前未标记的点中的dis的最小值的点x(s一开始就已经被标记),易得此距离即为s到x的最近距离,由x更新到其余未标记点的dis值,更新完后标记x;④反复进行上述步骤,直到所有点都被标记,即遍历完成。

图解(图源自《算法导论》)

在这里插入图片描述

优化:通过观察发现有一个步骤是取出dis最小的点,于是这里就可以通过堆优化来优化时间。

代码部分(非手写堆)
#include<bits/stdc++.h>
#define maxn 100500
using namespace std;
struct edge
{
    int to, dis, next;
}e[maxn];
int head[maxn], dis[maxn], cnt = 0;
bool vis[maxn];//标记数组
int n, m, s;


void add(int u, int v, int d){ // 点加入图 
	cnt++;
	e[cnt].to = v;
	e[cnt].dis = d;
	e[cnt].next = head[u];
	head[u] = cnt;
} 

struct node
{
    int dis;
    int pos;
    bool operator <( const node &x )const
    {
        return x.dis < dis;
    }
};

std::priority_queue<node> q; // 创建优先队列

void dijkstra(){
	for(int i = 1; i <= n; i++)
		dis[i] = 0x7fffffff;
	dis[s] = 0;
	q.push((node){0, s}); //出发点入队 
	while(!q.empty()){
		node tmp = q.top();
		q.pop();
		int pos = tmp.pos, d = tmp.dis;
		if(vis[pos])
			continue;
		vis[pos] = 1;
		for(int i = head[pos]; i; i = e[i].next){ //遍历与队首点连接的结点 
			int y = e[i].to;
            if(dis[y] > dis[pos] + e[i].dis){     
                dis[y] = dis[pos] + e[i].dis; //更新dis值 
                if(!vis[y])           
                    q.push(( node ){dis[y], y}); //该点及其dis值入队          
            }
		}		
	}
} 
int main(){
	int u, v, d;
	
	scanf("%d%d%d", &n, &m, &s);
	for(int i = 0; i < m; i++){
		scanf("%d%d%d", &u, &v, &d);
		add(u, v, d);
	}
	dijkstra();
	for(int i = 1; i <= n; i++)
		printf("%d ", dis[i]);
    return 0;
}
代码部分(手写堆)
#include<stdio.h>
#include<string.h>
#include<math.h>
#define maxn 1000005
#define inf 2e9
int n, m, s, cnt = 0, count = 0; 
struct edge{
    int next, to, dis;
}e[maxn];
int head[maxn], dis[maxn], vis[maxn];
struct node{ //定义优先队列元素 
	int pos;
	int d;
}a[maxn], tmp;
int read(){ //读入函数 
    int x = 0;
	char ch = getchar();
    while(ch < '0' || '9' < ch) 
		ch=getchar();
    while('0' <= ch && ch <= '9') 	
		x = (x << 3) + (x << 1) + (ch ^ '0'), ch = getchar();
    return x;
}
void add(int u, int v, int d){
	cnt += 1;
	e[cnt].dis = d;
	e[cnt].to = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}
void push(struct node x){ // 入队 
	int i, j;
	count += 1;
	a[count] = x;
	for(i = count, j = count >> 1; j; i = j, j >>= 1){
		if(a[i].d < a[j].d)
			tmp = a[i], a[i] = a[j], a[j] = tmp;
		else
			break;
	}
	return;
}
void pop(){ //出队 
	int i, j;
	a[1] = a[count--];
	for(i = 1, j = 2; j <= count; i = j, j <<= 1){
		if(a[j + 1].d < a[j].d)
			j += 1;
		if(a[i].d > a[j].d)
			tmp = a[i], a[i] = a[j], a[j] = tmp;
		else
			break;	
	}
	return;
}
void dijkstra(){
	int i, j;
	struct node x;
	for(i = 1; i <= n; i++)
		dis[i] = inf;
	dis[s] = 0;
	push((struct node){s, 0});
	while(count){
		x = a[1];
		pop();
		if(vis[x.pos])
			continue;
		vis[x.pos] = 1;
		for(i = head[x.pos]; i; i = e[i].next){
			if(dis[e[i].to] > dis[x.pos] + e[i].dis){ //权值更新 
				dis[e[i].to] = dis[x.pos] + e[i].dis;
                if(!vis[e[i].to]){
                	push((struct node){e[i].to, dis[e[i].to]});
				} 
			}
		}
	}
}
int main() {
	int i, j, k, t;
	int u, v, d;
	n = read(), m = read(), s = read();
	for(i = 1; i <= m; i++){
		u = read(), v = read(), d = read();
		add(u, v, d);
	}
	dijkstra();
	for(i = 1;i <= n; ++i)
		printf("%d ",dis[i]);
	return 0; 
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值