算法分析与设计——单源正权最短路Dijkstra算法


一、问题

  • 对于下图使用Dijkstra算法求由顶点a到顶点h的最短路径。
    在这里插入图片描述

二、解析

由顶点a到顶点h可以看出这是在一个有向图内从一个固定源点出发,找到其他任意一点的最短距离的问题,即单源最短路问题。

可以把a到h的各个顶点映射成1-8的编号,(x, y, z)表示从x出发到y的距离是z的边。维护一个记录a到任意一点i的最短路径的数组,初始默认都是无穷远。

从a点开始,找到它出边到达的点中离a最近的一个点x去访问,并标记x,再以此从x点往它的出边到达的某个点y试探,如果a到x再到y的距离比a直接到y的距离要小,就更新a到y的最短距离数组,再找到可以访问的点中离a最近的(已经更新过)的点进行标记,以此类推直到所有点都被标记。

三、设计

  1. 建立一个dist数组,dist[i]表示1到i的最短距离,虽然可能是多个节点中转过的最短距离,但可以看作直接从1到i的一条虚拟路径,dist[1]=0为起点,其他点设为无穷大;
  2. 找出一个尚未被标记的且dist[x]最小的节点x(即离起点最近的点),并标记x;
  3. 搜索节点x的所有出边(x, y, z),用min(dist[y], dist[x]+z) 来确保所有访问到的点y对应的dist[y]表示的是到a的最短路,就是比较先到x再到y和直接到y(直接路径可能不存在,可能是被同样的操作压缩出来的虚拟路径)哪个更短,哪个成为这条虚拟路径;
  4. 重复2.3的步骤,直到所有节点被标记。

四、分析

由于每个点都要用循环遍历一次,且每个点都要扫描其出边的点,所以需要两层循环,时间复杂度为O(n²)

因为要存储所有点出入的关系,用邻接矩阵存储的话空间复杂度为O(n²)

五、代码

#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<string.h>
#include<algorithm>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1005;

int a[N][N], dist[N], n, m;//dist[i]维护1到i点的最短距离,a[x][y]指x到y的距离
bool v[N];//标记访问过的节点

void Dijkstra() {
	memset(dist, INF, sizeof(dist));
	dist[1] = 0;
	for (int i = 1; i < n; i++) {
		int x = 0;
		//找到未标记节点中dist最小的,即离起点最近的点
		for (int j = 1; j <= n; j++) {
			if (!v[j] && (x == 0 || dist[j] < dist[x]))
				x = j;
		}
		v[x] = 1;
		//用当前的最短路点x对其他点进行最短路(那条1到i的虚拟路径)更新
		for (int y = 1; y <= n; y++) {
			dist[y] = min(dist[y], dist[x] + a[x][y]);
		}
	}
}

int main(void) {
	scanf("%d%d", &n, &m);//n是点数,m是边数
	memset(a, INF, sizeof(a));//连不到默认为无穷远
	for (int i = 1; i <= n; i++)
		a[i][i] = 0;//自己到自己的距离为0
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x][y] = min(a[x][y], z);//x到y可能不止一条,保留最短的即可
	}
	Dijkstra();
	
	for (int i = 1; i <= n; i++){
		printf("到点%d的最短路距离为:%d\n\n", i,dist[i]);
	}
	
	printf("故点a到点h的距离为:%d\n", dist[n]);
	
}
/*测试例子
8 11
1 2 1
2 4 2
3 1 2
4 3 1
4 6 8
5 4 2
6 5 2
5 7 2
7 6 3
7 8 3
8 6 2
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值