Dijkstra求最短路(邻接表存储,前向星存储,堆优化)

dijkstra算法主要用来求稀疏图的单源点最短路径,其核心是首先存储单源点到其它各点的路径,叫做估计最短径值,然后找出其中最短的一条路径,记录该点,那么此时该点的估计最短径值就变成了确定最短路径值。

例如:这里同dis数组来记录单源点1到其它各点最短路径值,下标从1开始,最初其值为 0 5 1 3 4.

此时得到dis[3] = 1为最小值,则可以确定源点到点3的最短路径,另外点3到点2有一条路径长度为2,容易知道,1+2=3 > 5,则此时可松弛点2的最短路径变为3。此时大致的思路已经出来了。

 

先来一个邻接矩阵存储边信息的Dijkstra,

 

#include <iostream>
#include <cstdio>
#define inf 10e7
using namespace std;
int main(){
	int a, b, c, k, i, j, n, m, l, min, jk[100][100], dis[100], book[100]={0};
	cin >> n >> m;
	for(i = 1; i <= n; ++i)
		for(j = 1; j <= n; ++j)
			if(i == j) jk[i][j] = 0;
			else jk[i][j] = inf;
	for(i = 1; i <= m; ++i){
		cin >> a >> b >> c;
		jk[a][b] = c;
	}
	for(i = 1; i <= n; ++i)
		dis[i] = jk[1][i];
	book[1] = 1;
	for(k = 1; k < n; ++k){
		min = inf;
		for(i = 1; i <= n; ++i)
			if(book[i] == 0 && min >= dis[i]) {
				min = dis[i];
				l = i;
			}
		book[l] = 1;
		for(i = 1; i <= n; ++i)
			if(jk[l][i] < inf && dis[i] > dis[l]+jk[l][i])
				dis[i] = dis[l]+jk[l][i];
	}
	for(i = 1; i <= n; ++i)
		printf("%d ", dis[i]);
	printf("\n");
	return 0;
}


再来一个数组实现的邻接表的Dijisra,

 

#include <iostream>
#include <cstring>
#include <cstdio>
#define inf 10e7
using namespace std;
int u[100], v[100], w[100], head[100], next2[100], dis[100], book[100]={0};
int main(){
	int k, i, j, n, m, l, min;
	cin >> n >> m;
	memset(head, -1, sizeof(head));
	for(i = 1; i <= m; ++i){
		cin >> u[i] >> v[i] >> w[i];
		next2[i] = head[u[i]];
		head[u[i]] = i;
	}
	for(i = 1; i <= n; ++i)
		dis[i] = inf;
	dis[1] = 0;
	k = head[1];
	while(k != -1){
		dis[v[k]] = w[k];
		k = next2[k];
	}
	book[1] = 1;
	for(j = 1; j < n; ++j){
		min = inf;
		for(i = 1; i <= n; ++i)
			if(book[i] == 0 && min >= dis[i]) {
				min = dis[i];
				l = i;
			}
		book[l] = 1;
		k = head[l];
		while(k != -1){
			if(w[k] < inf && dis[v[k]] > dis[l]+w[k])
				dis[v[k]] = dis[l]+w[k];
			k = next2[k];
		}
	}
	for(i = 1; i <= n; ++i)
		printf("%d ", dis[i]);
	printf("\n");
	return 0;
}


别小看了邻接表和邻接矩阵的差别,有时候题目是否可以AC就在于次,邻接矩阵空间复杂度为O(n^2),邻接表空间复杂度为O(M),对于稀疏图来说,两者的差距可想而知。

 

所以,再来一个类似邻接表的存储方式——前向星存储,它们的空间复杂度是差不多,

 

#include <iostream>
#include <cstring>
#include <cstdio>
#define inf 10e7
using namespace std;
int no, head[100], dis[100], book[100]={0};
struct node{
	int to, w, next;
} edge[100];
void add(int u, int v, int w){
	edge[no].to = v;
	edge[no].w = w;
	edge[no].next = head[u];
	head[u] = no++;
}
int main(){
	int a, b, c, k, i, j, n, m, l, min;
	cin >> n >> m;
	no = 1;
	memset(head, -1, sizeof head);
	for(i = 1; i <= m; ++i){
		cin >> a >> b >> c;
		add(a, b, c);
	}
	for(i = 1; i <= n; ++i)
		dis[i] = inf;
	dis[1] = 0;
	k = head[1];
	while(k != -1){
		dis[edge[k].to] = edge[k].w;
		k = edge[k].next;
	}
	book[1] = 1;
	for(j = 1; j < n; ++j){
		min = inf;
		for(i = 1; i <= n; ++i)
			if(book[i] == 0 && min >= dis[i]) {
				min = dis[i];
				l = i;
			}
		book[l] = 1;
		k = head[l];
		while(k != -1){
			if(edge[k].w < inf && dis[edge[k].to] > dis[l]+edge[k].w)
				dis[edge[k].to] = dis[l]+edge[k].w;
			k = edge[k].next;
		}
	}
	for(i = 1; i <= n; ++i)
		printf("%d ", dis[i]);
	printf("\n");
	return 0;
}

 

既然我们学到了图论,想必已经学过单链表了,所以博主无聊又用链式邻接表来实现了一番,

 

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define inf 10e7
using namespace std;
struct node{
	int v, w;
	node *next;
}*jk[105], *k, *pre;
int dis[100], book[100];
int main(){
	int n, m, u, v, w, min, i, j, l;
	memset(book, 0, sizeof book);
	cin >> n >> m;
	for(i = 1; i <= n; ++i)
		jk[i] = NULL;
	for(i = 1; i <= m; ++i){
		cin >> u >> v >> w;
		k = jk[u];
		pre = NULL;
		while(k != NULL) {
			pre = k;
			k = k->next;
		}
		k = (struct node*)malloc(sizeof(struct node));
		k->v = v;
		k->w = w;
		k->next = NULL;
		if(pre == NULL) jk[u] = k;
		else pre->next = k;
		// k = jk[v];			//如果是无向图,应加上这么一块
		// pre = NULL;
		// while(k != NULL) {
		// 	pre = k;
		// 	k = k->next;
		// }
		// k = (struct node*)malloc(sizeof(struct node));
		// k->v = w;
		// k->w = w;
		// k->next = NULL;
		// if(pre == NULL) jk[v] = k;
		// else pre->next = k;	
	}
	for(i = 1; i <= n; ++i)
		dis[i] = inf;
	dis[1] = 0;
	k = jk[1];
	while(k != NULL){
		dis[k->v] = k->w;
		k = k->next;
	}
	book[1] = 1;
	for(j = 1; j < n; ++j){
		min = inf;
		for(i = 1; i <= n; ++i)
			if(book[i] == 0 && min >= dis[i]) {
				min = dis[i];
				l = i;
			}
		book[l] = 1;
		k = jk[l];
		while(k != NULL){
			if(dis[k->v] > dis[l]+k->w)
				dis[k->v] = dis[l]+k->w;
			k = k->next;
		}
	}
	for(i = 1; i <= n; ++i)
		printf("%d ", dis[i]);
	printf("\n");
	return 0;
}			//如果是无向图,应加上这么一块
		// pre = NULL;
		// while(k != NULL) {
		// 	pre = k;
		// 	k = k->next;
		// }
		// k = (struct node*)malloc(sizeof(struct node));
		// k->v = w;
		// k->w = w;
		// k->next = NULL;
		// if(pre == NULL) jk[v] = k;
		// else pre->next = k;	
	}
	for(i = 1; i <= n; ++i)
		dis[i] = inf;
	dis[1] = 0;
	k = jk[1];
	while(k != NULL){
		dis[k->v] = k->w;
		k = k->next;
	}
	book[1] = 1;
	for(j = 1; j < n; ++j){
		min = inf;
		for(i = 1; i <= n; ++i)
			if(book[i] == 0 && min >= dis[i]) {
				min = dis[i];
				l = i;
			}
		book[l] = 1;
		k = jk[l];
		while(k != NULL){
			if(dis[k->v] > dis[l]+k->w)
				dis[k->v] = dis[l]+k->w;
			k = k->next;
		}
	}
	for(i = 1; i <= n; ++i)
		printf("%d ", dis[i]);
	printf("\n");
	return 0;
}

 

最后,就是我们的终极形态了,前向星存储+堆优化的DIjistra,为什么要用到堆优化呢,因为我们每次寻找最短的路径值的时候,需要花费O(N)的时间去寻找,用堆可优化至O(logN),也可别小瞧这俩之间的差距。一提到这儿,我又想起了昨天的比赛,就有这么一道最短路的题,刚开始我和队友用的是队列优化的Bellman-ford算法即SPFA,博主比较喜欢这个算法,但昨天的比赛的数据点卡住SPFA了,昨天的题是在第49个点上TLE(超时)的,用堆优化的Dijistra才AC了那道题...博主才学疏浅,希望神牛不要嘲笑,好,下面贴出我们的代码,

 

#include <iostream>  
#include <string.h>  
#include <cstdio>  
#include <queue>  
#define inf 0x3f3f3f3f  
using namespace std;  
int head[100], dis[100], book[100], pre[100];  
struct note{  
    int v, w, next;  
} edge[100*100];  
struct node{  
    int v, w;  
    node(int a, int b){  
        w = a;  
        v = b;  
    }  
    friend bool operator< (node a, node b){  
        return a.w > b.w;  
    }  
};  
int n, m, no;  
priority_queue<node> q;  
void add(int u, int v, int w){  
    edge[no].v = v;  
    edge[no].w = w;  
    edge[no].next = head[u];  
    head[u] = no++;  
}  
void init(){  
    no = 0;  
    memset(head, -1, sizeof head);
    memset(dis, 0x3f, sizeof dis);
    memset(book, 0, sizeof book);
    memset(pre, -1, sizeof pre);
}  
void Dijkstra(){  
    while(!q.empty()) q.pop(); //将声明放在bss区(全局区),减少栈内存的消耗,此句清空队列中数据   
    dis[1] = 0;  
    q.push(node(0, 1));  
    while(!q.empty()){  
        node x = q.top();  
        q.pop();  
        if(book[x.v]) continue;  
        book[x.v] = 1;  
        int k = head[x.v];  
        while(k != -1){  
            if(dis[edge[k].v] > dis[x.v]+edge[k].w){  
                dis[edge[k].v] = dis[x.v]+edge[k].w;
                pre[edge[k].v] = x.v;  
                q.push(node(dis[edge[k].v], edge[k].v));  
            }  
            k = edge[k].next;  
        }  
    }  
} 
void PrintPath(int end)
{
	int k = end;
	while(k != -1)
	{
		printf("%d ", k);
		k = pre[k];
	}
	printf("\n");
} 
int main(){  
    int a, b, c, i;  
    scanf("%d %d", &n, &m); 
    init();  
    for(i = 1; i <= m; ++i){  
        scanf("%d %d %d", &a, &b, &c);  
        add(a, b, c);   //无向图   
        add(b, a, c);  
    }  
    Dijkstra();   
    for(i = 1; i <= n; ++i)  
        printf("%d ", dis[i]);  
    printf("\n");  
    PrintPath(5);	//打印路径
    return 0;  
}
/*
样例:
5 5
1 2 2
1 3 3
2 4 4
3 4 2
4 5 1
*/

 

 

 

最后一份代码是最终的Dijistra了,如果大家想了解适合稠密图的floyed-Warshell算法,可看上一篇文章,想了解Bellman-ford算法及其队列优化的朋友可看下一篇文章。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值