最小生成树-普利姆算法eager实现
算法描述
在普利姆算法的lazy实现中,参考:普利姆算法的lazy实现
我们现在来考虑这样一个问题:
我们将所有的边都加入了优先队列,但事实上,我们真的需要所有的边吗?
我们再回到普利姆算法的lazy实现,看一下这个问题:
当顺着顶点0的邻接表考察顶点7时,边7-2和边7-1被加入了优先队列Q.
然而,当我们开始对顶点2进行考察时:
边2-3是最轻边,我们显然不需要对边7-2和边7-1进行再次考察.
但是,由于边7-2和边7-1在对顶点2进行考察之前已经加入了优先队列Q,似乎我们对之前发生的事无可奈何,也必须让优先队列维护着这些不再候选的废边,从而加重了优先队列的负担,影响了效率.
结果是否真的如此?
如果我们仔细思考,会注意到我们可以采取这样的一个技巧去防止将废边加入优先队列:
我们关注的只是当前能看到的最轻边,所以边7-2和边7-1对我们来说只有这样的意义:
边7-2:到顶点2的距离是x;
边7-1:到顶点2的距离是y;
边3-2:到顶点2的距离是z.
而z > x且z >y.
所以我们既然无法避免在先于顶点2之前就将边7-2和边7-1当做废边(贪心算法),所以我们可以
采取更新的方式来在优先队列Q中维护到某个顶点的最短距离.
换句话说,我们对某个顶点,只在Q中维护一条边,就是当前已知连着它的最轻边.
由此,我们避免了将所有的边都加入优先队列Q,从而使得最差情况下Q的操作与图的顶点数V 成线性渐进:O(V ).
但一般的优先队列只提供了入队(enqueue)和出队(dequeue)操作,要更新到某个顶点的最短距离,我们需要高效地在优先队列中访问这个顶点.
那么按照一般优先队列的方式,比如jdk中的优先队列,它会是这样:
private int indexOf(Object o) {
if (o != null) {
for (int i = 0; i < size; i++)
if (o.equals(queue[i]))
return i;
}
return -1;
}
这虽然可以帮助我们在队列中找到元素,但这显然不高效.
有没有一种办法可以按常量时间来找到所需元素?
答案是:索引(index),由此:
我们需要一个对顶点在队列中的索引.
这可以保证我们以常量的时间在队列中找到顶点.
关于索引式优先队列及实现可以参考:带索引的优先队列
实现分析
万事具备,那么我们对某顶点的邻接点(或邻接的边)的遍历和处理就会是这样:
private void search(int src) {
IndexPriorityQueue<Double> q = indexCrossingEdges;
visited[src] = true;
//遍历邻接的边
for(Edge edge:g.vertices()[src].Adj) {
WeightedEdge we = (WeightedEdge)edge;
int to = we.to;
if(visited[to])
continue;
//到顶点to的距离可以改善了
if(we.weight < distanceTo[to]) {
distanceTo[to] = we.weight;
lastEdgeTo[to] = we;
if(q.contains(to)) {
//我们在队列中只维护一条到某个顶点的距离
//在我们可以改善到这个顶点的距离是,我们更新它
q.decreaseKey(to, distanceTo[to]);
}else {
q.offer(to, distanceTo[to]);
}
}
}
}
}
算法一开始的时候,我们从源点v出发,将其加入队列Q,然后开始进行mst的建立工作:
private void mst(int v) {
IndexPriorityQueue<Double> q = indexCrossingEdges;
distanceTo[v] = 0.0d;
q.offer(v, distanceTo[v]);
while (!q.isEmpty()) {
int src = q.poll();
search(src);
}
}
完整实现
普利姆算法的完整eager实现如下,其中的一些类和字段不明白的
请参考:普利姆算法的lazy实现
/**
* Created by 浩然 on 4/21/15.
*/
public class EagerPrim extends LazyPrim {
protected WeightedEdge[] lastEdgeTo;
/**
* 索引式优先队列,用于维护crossing edges
* 用于在eager普利姆算法中高效返回最轻边并支持decrease-key操作
*/
protected IndexPriorityQueue<Double> indexCrossingEdges;
public EagerPrim(WeightedUndirectedGraph g) {
super(g);
}
@Override
protected void resetMemo() {
super.resetMemo();
lastEdgeTo = new WeightedEdge[g.vertexCount()];
//重置优先队列
indexCrossingEdges = new IndexPriorityQueue<>();
}
private void setupMST() {
for (int v = 0; v < lastEdgeTo.length; v++) {
WeightedEdge we = lastEdgeTo[v];
if (we != null) {
mst.offer(we);
mstWeight += we.weight;
}
}
}
/**
* eager-prim算法,时间复杂度为最差O(ElogV)
*/
@Override
public void performMST() {
resetMemo();
//对图中的所有顶点进行遍历,可以找出MSF(最小生成森林)
//这里我们假设图是连通的,所以可以找出一棵MST
mst(0);
setupMST();
}
private void mst(int v) {
IndexPriorityQueue<Double> q = indexCrossingEdges;
distanceTo[v] = 0.0d;
q.offer(v, distanceTo[v]);
while (!q.isEmpty()) {
int src = q.poll();
search(src);
}
}
private void search(int src) {
IndexPriorityQueue<Double> q = indexCrossingEdges;
visited[src] = true;
//遍历邻接的边
for(Edge edge:g.vertices()[src].Adj) {
WeightedEdge we = (WeightedEdge)edge;
int to = we.to;
if(visited[to])
continue;
//到顶点to的距离可以改善了
if(we.weight < distanceTo[to]) {
distanceTo[to] = we.weight;
lastEdgeTo[to] = we;
if(q.contains(to)) {
//我们在队列中只维护一条到某个顶点的距离
//在我们可以改善到这个顶点的距离是,我们更新它
q.decreaseKey(to, distanceTo[to]);
}else {
q.offer(to, distanceTo[to]);
}
}
}
}
}
时间复杂度
由于避免了对废弃边的访问,所以在优先队列中最多维护V条记录.
优先队列的操作耗时O(logV ).
遍历所有边的操作耗时O(E ),则整体耗时O(ElogV)
最小生成树-普利姆算法eager实现的更多相关文章
- 最小生成树-普利姆算法lazy实现
算法描述 lazy普利姆算法的步骤: 1.从源点s出发,遍历它的邻接表s.Adj,将所有邻接的边(crossing edges)加入优先队列Q: 2.从Q出队最轻边,将此边加入MST. 3.考察此边的 ...
- 最小生成树-普利姆(Prim)算法
最小生成树-普利姆(Prim)算法 最小生成树 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一种特殊的图),或者 ...
- 图论---最小生成树----普利姆(Prim)算法
普利姆(Prim)算法 1. 最小生成树(又名:最小权重生成树) 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一 ...
- POJ-2421-Constructing Roads(最小生成树 普利姆)
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 26694 Accepted: 11720 Description The ...
- 普利姆算法(prim)
普利姆算法(prim)求最小生成树(MST)过程详解 (原网址) 1 2 3 4 5 6 7 分步阅读 生活中最小生成树的应用十分广泛,比如:要连通n个城市需要n-1条边线路,那么怎么样建设才能使工程 ...
- 图->连通性->最小生成树(普里姆算法)
文字描述 用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价.对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可 ...
- 最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)
普里姆算法(Prim算法) #include<bits/stdc++.h> using namespace std; #define MAXVEX 100 #define INF 6553 ...
- 算法与数据结构(五) 普利姆与克鲁斯卡尔的最小生成树(Swift版)
上篇博客我们聊了图的物理存储结构邻接矩阵和邻接链表,然后在此基础上给出了图的深度优先搜索和广度优先搜索.本篇博客就在上一篇博客的基础上进行延伸,也是关于图的.今天博客中主要介绍两种算法,都是关于最小生 ...
- HDU 1879 继续畅通工程 (Prim(普里姆算法)+Kruskal(克鲁斯卡尔))
继续畅通工程 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...
随机推荐
- restful API 规范(转)
1. URI URI 表示资源,资源一般对应服务器端领域模型中的实体类. URI规范 不用大写: 用中杠-不用下杠_: 参数列表要encode: URI中的名词表示资源集合,使用复数形式. 资源集合 ...
- matlab转python
最近在做把matlab代码转成python代码,没有用过matlab,python也只是局限于爬虫,所以.... matlab与python最大的不同是,matlab的下标是从1开始的,python和 ...
- python 常用的标准库及第三方库
标准库Python拥有一个强大的标准库.Python语言的核心只包含数字.字符串.列表.字典.文件等常见类型和函数,而由Python标准库提供了系统管理.网络通信.文本处理.数据库接口.图形系统.XM ...
- 20155309 2016-2017-2《Java程序设计》课程总结
预备作业1http://www.cnblogs.com/nhx19970709/p/6155580.html 第一次写博客,也是第一次用Markdown,具体流程都还不是很熟悉 预备作业2http:/ ...
- 一键复制功能 - Vue
经常遇到一键复制功能,简单记录一下.这里使用的是clipboard插件:https://clipboardjs.com/ 第一步 安装:npm install clipboard --save 第二步 ...
- dos批处理知识
echo 命令 rem 命令 pause 命令 call 命令 start 命令 goto 命令 set 命令 编辑本段批处理符号简介 回显屏蔽 重定向1 与 重定向2 管道符号 转义符 逻辑命令符 ...
- Zabbix监控websphere和weblogic
本节内容 zabbix java gateway 配置和运行java gateway 配置zabbix server使用java gateway 调整java gateway的日志级别 监控weblo ...
- 书接前文,用多进程模式实现fibonnachi并发计算
#coding: utf-8 import logging import os import random import sys import time import re # import requ ...
- maven:missing artifact jdk.tools:jar:1.7
http://stackoverflow.com/questions/11118070/buiding-hadoop-with-eclipse-maven-missing-artifact-jdk-t ...
- Warning -27077: The "vuser_init" section contains web function(s) when the "Simulate a new user on each iteration" Run-Time Setting is ON.
通过LR来录制登录过程并生成脚本,设置了自动关联,并回放录制脚本,观察回放日志发现没有报error信息,说明脚本没有问题,将脚本放入Controller中设置100个用户设置运行,发现运行一段时间开始 ...