• 最小生成树-普利姆算法lazy实现


    算法描述

    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<>();
        }
    }
    【版权所有@foreach_break】 【博客地址 http://www.cnblogs.com/foreach-break】 可以转载,但必须注明出处并保持博客超链接
  • 相关阅读:
    软件测试的方法
    常用的adb
    正则表达式
    Python学习笔记(九)————进程和线程
    CodeForces
    华为FusionSphere openstack安装
    华为FusionCompute单节点安装教程--VRM主机的安装
    华为FusionCompute单节点安装教程--CNA主机的安装
    华为FusionSphere--存储管理
    华为FusionSphere--架构介绍
  • 原文地址:https://www.cnblogs.com/foreach-break/p/4471193.html
Copyright © 2020-2023  润新知