最短路径树与最小生成树区别与联系(以dijkstra和prim为例)

区别 

dijkstra求单源最短路径时,选择的边组成的树叫做最短路径树;prim是求最小生成树的。

struct Edge {
	int next;
	int cost;
};
vector<Edge> graph[MAXN]; //邻接表
int d[MAXN]; //当前从源点到该结点最短路径
bool mark[MAXN]; //结点最短路径是否确定

void dijkstraOrPrim(int n, int s) { //顶点编号0到n - 1, 源点s
	for (int i = 0; i < n; i++) {
		d[i] = -1;
		mark[i] = false;
	}
	int newp = s; //最小生成树可以以任意结点作为源点
	d[newp] = 0;
	mark[newp] = true;
	for (int i = 1; i < n; i++) { //循环n - 1次(源点不用), 每次确定一个顶点的最短路径
		//(1)此时newp是上一次确定最短路径的顶点, 考虑newp的影响
		for (int j = 0; j < graph[newp].size(); j++) {
			int next = graph[newp][j].next;
			int cost = graph[newp][j].cost;
			if (mark[next]) //已经确定的不再计算
				continue;
			/*==============================================================*/
			/*dijkstra与prim只有下面一行代码不同, 其他地方含义可能不同, 但代码相同*/
			/*==============================================================*/
			if (d[next] == -1 || d[next] > d[newp] + cost) { //dijkstra
			//if (d[next] == -1 || d[next] > cost) //prim
				d[next] = d[newp] + cost;
			}
		}
		//(2)寻找当前最短路径or距离最小的点, 保存为新newp
		int mind = 1 << 30;
		for (int j = 0; j < n; j++) {
			if (mark[j] || d[j] == -1) //已经确定或仍不可达
				continue;
			if (mind > d[j]) {
				mind = d[j];
				newp = j;
			}
		}
		//(3)确定该点最短路径or加入最小生成树
		mark[newp] = true;
	}
}

 可以看出,单源最短路径与最小生成树选择边的区别就是dijkstra要距离源点最近,而prim只需要距离集合最近。体现在代码上就是dijkstra中d[next]大于d[newp] + cost时才更新,而prim中大于cost就更新。

联系

                                          图1: 最小生成树没选用的边中,通过该边长度更小

                               图2: 最小生成树没选用的边中,有与最小生成树中某一条边相同的

最短路径树如果与最小生成树不同,必然会弃用最小生成树中的某条边,而使用其余的某条边。 

最小生成树与最短路径树中边的对应情况如下,某一最小生成树没选用的边中:

  • 该最小生成树中最大边小的:
    1. 最小生成树因为有回路而没选用的边(可能与最小生成树中某条边相等,也可能都不等),最短路径树也一定不会选用,因为一旦选用必有回路。
    2. 有多棵最小生成树,当前这棵不是从所求源点出发的最短路径树(多棵最小生成树中可替换的边长度一定相同),判断某条边是否是这种情况时,先查找所有最小生成树中与该边长度相同的,再进行替换,如图2。
  • 该最小生成树中最大边大的:
    1. 如果通过这些边到达某一结点时,比通过最小生成树到达该结点要小,则最短路径树就会弃用最小生成树中这条边,转而选择该边,如图1。

当图中的边可以保证没有上图两种情况,也就不会导致选用其他的边,那么最短路径树就是最小生成树。比如下题:

N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离

 该题因为道路长度呈2的幂次倍增加,任意一条边,比小于它的所有边相加还要大。

比最小生成树中最大边大的,每一个都比最小生成树中所有边相加都大,有它们组成的路径,一定比最小生成树中路径长度大,所以最短路径树一定不会选用,图1情况排除;且每一条边都不同,最小生成树唯一,图2情况排除,所以它从任意源点出发的最短路径树一定是最小生成树。

但这道题K较大,需要取模,就不能用prim算法求最小生成树,加减乘运算才可以先取模再运算,比较大小不可以。prim算法要比较d[next]与cost大小,直到比较完毕之前都不能取模,要保留完整数值,可能导致数据过大而需要使用高精度。

kruskal算法先对边排序,排序之前不能取模,之后就不存在比较运算了。这道题边按大小顺序读入,省去了排序过程,可以读入边时直接取模,用kruskal建立最小生成树,该最小生成树是所有源点的最短路径树,从源点开始dfs最短路径树,到达某点所经过的边,权值相加就是到该点的最短路径。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值