算法描述

lazy普利姆算法的步骤

1.从源点s出发,遍历它的邻接表s.Adj,将所有邻接的边(crossing edges)加入优先队列Q;

2.从Q出队最轻边,将此边加入MST.

3.考察此边的两个端点,对两个端点重复第1步.

示例

从顶点0开始,遍历它的邻接表:边0-7、0-2、0-4、0-6会被加入优先队列Q.

顶点0的邻接表搜索完毕后,边0-7是最轻边,所以它会出队,并加入MST.

如下图:



边0-7出队后,开始考察边的两个端点:

顶点0已经访问过了,跳过;

顶点7还未探索,开始探索顶点7.对7的邻接表进行访问和第一步类似.

我们找到最轻边7-1并加入MST

如下图:

对每条边重复,当所有边都考察完毕,我们就得到了最小生成树,如下图:

时间复杂度

扫描所有边会耗时O(E ).

由于所有的边都会入队,优先队列调整的操作耗时O(logE ).

那lazy方式最差就是O(ElogE ).

其中E 是图的边数.

算法实现

算法的第一步,将源点s所有的邻接的边加入Q,如下:

    /**
* 找出从源点出发的所有的crossing edges,并用一个优先队列维护他们
*
* 原理:
* 将对未访问的邻接点进行遍历当作一次切断(graph-cut),则源点和邻接点间的边就是crossing edge
* 根据贪心策略求MST的要求,要加入的边必须是最轻边(权重最小的边),
* 故而将crossing edges加入优先队列,这样便可用O(logN)的时间找出最小权重边
*
* @param src 源点
*/
private void search(int src) {
visited[src] = true;
for(Edge e : g.vertices()[src].Adj) {
WeightedEdge we = (WeightedEdge)e;
if(!visited[we.to])
crossingEdges.offer(we);
}
}

算法的第二步和第三步如下:

    /**
* lazy普利姆算法中,从一个源点出发
* 1:通过对源点的所有邻接点进行遍历的方式找出所有crossing edges
* 2:将crossing edges中最轻的(拥有最小的权重)边加入MST
* 3:将最轻边的另一个顶点作为源点,重复1-2步.
*
*
* @param src 源点
*/
private void mst(int src) {
search(src);
while (!crossingEdges.isEmpty()) {
WeightedEdge we = crossingEdges.poll();
//懒惰方式处理不再候选的边
if(visited[we.src] && visited[we.to])
continue;
//加入最小生成树
mst.offer(we);
//累积mst的权重
mstWeight += we.weight;
//向src的邻接点方向搜索
if(!visited[we.src])
search(we.src);
//向to的邻接点方向搜索
if(!visited[we.to])
search(we.to);
}
}

其中,维护所有crossing edge的,是一个优先队列:

    /**
* 优先队列,用于维护crossing edges
* 高效返回最轻边
*/
protected PriorityQueue<WeightedEdge> crossingEdges;

带权边的定义很简单,像下面这样:

import java.util.Comparator;

/**
* Created by 浩然 on 4/19/15.
* 带权边
*/
public class WeightedEdge extends Edge implements Comparable<WeightedEdge> {
/**
* 边的权重
*/
public Double weight; /**
* 边的源点
*/
public int src; /**
* 构造一条带权边
*@param src 源点
* @param other 目标点
* @param weight 权重
*/
public WeightedEdge(int src,int other, Double weight) {
super(other);
this.src = src;
this.weight = weight;
} /**
* 比较两条边的大小,这里作升序
* @param to 被比较边
* @return 如果权重比被比较的边小则返回-1,如果大于则返回1,相等则返回0
*/
@Override
public int compareTo(WeightedEdge to) {
if(this.weight < to.weight)
return -1;
else if(this.weight > to.weight)
return 1;
return 0;
}
} public class Edge {
public int to;
public Edge(int key) {
this.to = key;
}
}

而顶点的定义更简单,如下:

public class Vertex {
/**
* 邻接表
*/
public LinkedList<Edge> Adj;
}

完整代码

public class LazyPrim extends Algorithm {

    /**
* 优先队列,用于维护crossing edges
* 高效返回最轻边
*/
protected PriorityQueue<WeightedEdge> crossingEdges; public LazyPrim(WeightedUndirectedGraph g) {
super(g);
} /**
* lazy普利姆算法求MST或MSF(Minimum Spanning Forest最小生成森林)
*
* 算法复杂度:最差O(ElogE)
*/
public void performMST() {
resetMemo();
//对图中的所有顶点进行遍历,找出MST或MSF
for(int i = 0; i < g.vertexCount();i++){
if(!visited[i]) {
mst(i);
}
}
} /**
* lazy普利姆算法中,从一个源点出发
* 1:通过对源点的所有邻接点进行遍历的方式找出所有crossing edges
* 2:将crossing edges中最轻的(拥有最小的权重)边加入MST
* 3:将最轻边的另一个顶点作为源点,重复1-2步.
*
*
* @param src 源点
*/
private void mst(int src) {
search(src);
while (!crossingEdges.isEmpty()) {
WeightedEdge we = crossingEdges.poll();
//懒惰方式处理不再候选的边
if(visited[we.src] && visited[we.to])
continue;
//加入最小生成树
mst.offer(we);
//累积mst的权重
mstWeight += we.weight;
//向src的邻接点方向搜索
if(!visited[we.src])
search(we.src);
//向to的邻接点方向搜索
if(!visited[we.to])
search(we.to);
}
} /**
* 找出从源点出发的所有的crossing edges,并用一个优先队列维护他们
*
* 原理:
* 将对未访问的邻接点进行遍历当作一次切断(graph-cut),则源点和邻接点间的边就是crossing edge
* 根据贪心策略求MST的要求,要加入的边必须是最轻边(权重最小的边),
* 故而将crossing edges加入优先队列,这样便可用O(logN)的时间找出最小权重边
*
* @param src 源点
*/
private void search(int src) {
visited[src] = true;
for(Edge e : g.vertices()[src].Adj) {
WeightedEdge we = (WeightedEdge)e;
if(!visited[we.to])
crossingEdges.offer(we);
}
} @Override
protected void resetMemo() {
super.resetMemo();
//重置优先队列
crossingEdges = new PriorityQueue<>();
}
}

最小生成树-普利姆算法lazy实现的更多相关文章

  1. 最小生成树-普利姆算法eager实现

    算法描述 在普利姆算法的lazy实现中,参考:普利姆算法的lazy实现 我们现在来考虑这样一个问题: 我们将所有的边都加入了优先队列,但事实上,我们真的需要所有的边吗? 我们再回到普利姆算法的lazy ...

  2. 最小生成树-普利姆(Prim)算法

    最小生成树-普利姆(Prim)算法 最小生成树 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一种特殊的图),或者 ...

  3. 图论---最小生成树----普利姆(Prim)算法

    普利姆(Prim)算法 1. 最小生成树(又名:最小权重生成树) 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一 ...

  4. POJ-2421-Constructing Roads(最小生成树 普利姆)

    Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26694   Accepted: 11720 Description The ...

  5. 普利姆算法(prim)

    普利姆算法(prim)求最小生成树(MST)过程详解 (原网址) 1 2 3 4 5 6 7 分步阅读 生活中最小生成树的应用十分广泛,比如:要连通n个城市需要n-1条边线路,那么怎么样建设才能使工程 ...

  6. 图->连通性->最小生成树(普里姆算法)

    文字描述 用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价.对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可 ...

  7. 最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

    普里姆算法(Prim算法) #include<bits/stdc++.h> using namespace std; #define MAXVEX 100 #define INF 6553 ...

  8. 算法与数据结构(五) 普利姆与克鲁斯卡尔的最小生成树(Swift版)

    上篇博客我们聊了图的物理存储结构邻接矩阵和邻接链表,然后在此基础上给出了图的深度优先搜索和广度优先搜索.本篇博客就在上一篇博客的基础上进行延伸,也是关于图的.今天博客中主要介绍两种算法,都是关于最小生 ...

  9. HDU 1879 继续畅通工程 (Prim(普里姆算法)+Kruskal(克鲁斯卡尔))

    继续畅通工程 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

随机推荐

  1. overridePendingTransition()使用

    实现两个 Activity 切换时的动画.在Activity中使用有两个参数:进入动画和出去的动画. 注意1.必须在 StartActivity()  或 finish() 之后立即调用.2.而且在 ...

  2. WGS84转大地2000

    1.创建自定义地理(坐标)变换: 2.选择源坐标系和目标坐标系: 3.自定义地理转换方法,选择COORDINATE_FRAME; 4.选择投影工具: 5.在地理变换处选择刚才自定义变换.

  3. python基础--面向对象

    什么是面向对象编程 OOP编程是利用“类”和对象来创建各种模型来实现对真实世界的描述. OOP具有可维护性和可扩展性 二:面向对象有那些特性 1)CLASS类:一个类是对拥有相同属性的对象的抽象.类拥 ...

  4. day10作业

    1.Java中,用{}括起来的代码称为代码块. 代码块分为局部代码块,构造代码块,静态代码块,同步代码块 局部代码块:在方法中出现,限定生命周期,及早释放,提高内存利用率 构造代码块:在类中方法外出现 ...

  5. MySQL学习笔记:repeat、loop循环

    一.repeat循环 # ---- repeat ---- DELIMITER $$ CREATE PROCEDURE test_repeat() BEGIN ; REPEAT ; UNTIL a E ...

  6. python中round(四舍五入)的坑

    python中的round函数不能直接拿来四舍五入,一种替代方式是使用Decimal.quantize()函数. 具体内容待补. >>> round(2.675, 2) 2.67 可 ...

  7. Excel根据单元格内容设置整行颜色

    1. 选择需要设置的区域,条件格式中找到“新建规则” 2. 弹出窗口中选择“使用公式确定要设置格式的单元格”一项.填写公式如下: =IF(OR($D1="已完成",$D1=&quo ...

  8. CCF CSP 201604-2 俄罗斯方块

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201604-2 俄罗斯方块 问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游 ...

  9. Hive(七)Hive分析窗口函数

    一数据准备 cookie1,2015-04-10,1 cookie1,2015-04-11,5 cookie1,2015-04-12,7 cookie1,2015-04-13,3 cookie1,20 ...

  10. day7 面向对象进阶

    面向对象高级语法部分 通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例 ...