加权无向图 最小生成树 Prim算法 延迟版 村里修路该先修哪

 本次要解决的问题是:你们村里那些坑坑洼洼的路,到底哪些路才是主干道?

小明:肯定是哪里都能到得了,并且去哪里都相对比较近,并且被大家共用程度高的路是啊!

具体是哪几条路呢?今天就可以给出准确答案

最小生成树的特点

1。可以到达图中任何一个顶点

2. 是一颗树(无环)

3. 最小生成树的边的权重之和是可以链接图中所有顶点的边的集合中,权值之和最小的(运用了贪婪算法思想)

4. 边数 = 图的顶点数量-1


先看主要代码,再看库代码

//Prim算法的 最小生成树
//时间 ElogE                              E为遍历原图中每条边,logE为优先队列(二叉堆)找到最小权重边的平均成本
//空间 V-1条Edge + V个顶点
public class LazyPrimMST implements MST {
    EdgeWeightedGraph orgEwg;    //原始加权图
    EdgeWeightedGraph singleEwg; //只有最小生成树的加权图
    List<Edge> edges;            //最小生成树的边
    boolean[] marked;            //顶点的访问
    float weightSum;

    public LazyPrimMST(EdgeWeightedGraph ewg) {
        this.orgEwg = ewg;
        edges = new LinkedList<>();
        marked = new boolean[ewg.v];
        weightSum = 0;

        //只考虑一个连通图的情况
        //改进:排除有多个子图的情况
        //假设 ewg 是连通的

        BinHeap2<Edge> pqedges = new BinHeap2<Edge>();
        //最小生成树的性质:边数 = 图的顶点数量-1

        visit2(pqedges, 0);
        while (!pqedges.isEmpty()) {
            Edge e = pqedges.pop();
            int v = e.either();
            int w = e.other(v);
            if (marked[v] && marked[w])                      //已失效的横切边不处理
                continue;
            edges.add(e);                                    //将权重最小的加入到MST中
            if (!marked[v]) visit2(pqedges, v);
            if (!marked[w]) visit2(pqedges, w);
        }

        //将找到的最小生成树转换为一个 EWG对象
        singleEwg = new EdgeWeightedGraph(orgEwg.v());
        for (Edge e : edges) {
            singleEwg.addEdge(e);
        }
        this.orgEwg = null;
    }

    private void visit2(BinHeap2<Edge> pqedges, int v) {
        marked[v] = true;                                   //将访问顶点加入MST中
        for (Edge e : orgEwg.adj(v))
            if (!marked[e.other(v)]) pqedges.add(e);
    }

    //1.将关注顶点 周围的边加入 横切边集合
    //2.找到横切边集合中权重最小的边
    //3.将改边的对面顶点作为下一个关注顶点返回
    private int visit(BinHeap2<Edge> pqedges, int v) {
        marked[v] = true;                                   //将当前关注点加入最小生成树
        for (Edge e : orgEwg.adj(v)) {                      //加入关注顶点的边到优先队列, 横切边集合
            if (!marked[e.other(v)])                        //只加入未失效的横切边
                pqedges.add(e);
        }

        Edge tmpe = null;
        while (tmpe == null && !pqedges.isEmpty()) {
            tmpe = pqedges.pop();
            if (marked[tmpe.either()] && marked[tmpe.other(tmpe.either())])
                tmpe = null;                                //失效的横切边
        }
        if (tmpe == null)                                   //没有足够的边
            return -1;

        edges.add(tmpe);                                    //将最小权重的边加入到最小生成树
        if (!marked[tmpe.either()])                         //从最小权重的边里,找到未探索的对面顶点作为新的关注点
            v = tmpe.either();
        else
            v = tmpe.other(tmpe.either());
        return v;
    }

    @Override
    public Iterable<Edge> edges() {
        return edges;
    }

    @Override
    public float weight() {
        if (weightSum == 0) {
            for (Edge e : edges) {
                weightSum += e.weight();
            }
        }
        return weightSum;
    }

    public EdgeWeightedGraph getSingleEWGraph() { //只保留最小生成树的加权有向图
        return singleEwg;
    }

    public static void main(String[] args) {
        // 村口         二狗子家
        // 0--------------1
        // |\            /|
        // | \   你家    / |
        // |  -----2----  |
        // |              |
        // +---------3----+
        //          希望小学

        EdgeWeightedGraph ewg = new EdgeWeightedGraph(4);
        ewg.addEdge(0, 1, 2,"二麻二麻路");
        ewg.addEdge(0, 2, 3,"挨打巷西段");
        ewg.addEdge(0, 3, 4,"挨打巷东段");
        ewg.addEdge(1, 2, 3.5f,"恶犬巷");
        ewg.addEdge(1, 3, 2.5f,"希望之路");
        System.out.println(ewg);
        System.out.println("=======");

        LazyPrimMST lp = new LazyPrimMST(ewg);
        System.out.println("最小生成树权重总和(村里主干道总长度): " + lp.weight());
        for (Edge e : lp.edges()) {
            System.out.println(e.either() + "和" + e.other(e.either()) + "之间的路["+e.name+"], 路长:" + e.weight());
        }
    }
}

输出

最小生成树权重总和(村里主干道总长度): 7.5
0和1之间的路[二麻二麻路], 路长:2.0
1和3之间的路[希望之路], 路长:2.5
0和2之间的路[挨打巷西段], 路长:3.0

库代码: 

//加权无向图
public class EdgeWeightedGraph {
    LinkedList<Edge>[] edges;               //边的集合
    final int v;                            //顶点数量
    int e;                                  //边的数量

    public EdgeWeightedGraph(int v) {
        this.v = v;
        this.e = 0;
        edges = new LinkedList[v];
        for (int i = 0; i < v; i++)
            edges[i] = new LinkedList<>();
    }

    public void addEdge(int v, int w, float weight) {
        addEdge(new Edge(v, w, weight));
    }

    public void addEdge(Edge e) {           //添加一条边,在无向图中等于向2边顶点添加边(互相连通)
        edges[e.v].add(e);
        edges[e.w].add(e);
        this.e++;
    }

    public Iterable<Edge> adj(int v) {
        return edges[v];
    }

    //返回所有边的集合
    public Iterable<Edge> edges() {
        List<Edge> es = new LinkedList<>();
        for (int v = 0; v < edges.length; v++) {
            for (Edge e : adj(v)) {
                if (e.other(v) > v)           //加入顺序: 顶点序号逆序
                    es.add(e);
            }
        }
        return es;
    }

    public int v() {//顶点数
        return v;
    }

    public int e() {//边数
        return e;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int v = 0; v < v(); v++) {
            sb.append(v);
            sb.append(": ");
            for (Edge e : adj(v)) {
                sb.append(e.toString());
                sb.append(", ");
            }
            sb.append('\n');
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        EdgeWeightedGraph ewg = new EdgeWeightedGraph(4);
        ewg.addEdge(0, 1, 1);
        ewg.addEdge(0, 2, 2);
        ewg.addEdge(0, 3, 3);
        ewg.addEdge(1, 2, 3);
        System.out.println(ewg);
    }
}

边对象

public class Edge extends Vertex implements Comparable<Edge> {
    public int w;
    public float weight;
    public String name;

    //from - to
    public Edge(int v, int w, float weight) {
        super(v);
        this.w = w;
        this.weight = weight;
    }

    public Edge(int v, int w, float weight, String name) {
        super(v);
        this.w = w;
        this.weight = weight;
        this.name = name;
    }

    public float weight() {
        return weight;
    }

    public int either() {
        return v;
    }

    public int other(int vertex) {
        if (vertex == this.v) return w;
        else if (vertex == this.w) return v;

        throw new RuntimeException("no such vertex " + vertex + " ,[v:" + v + " w:" + w + "]");
    }

    @Override
    public int compareTo(@NonNull Edge another) {
        if (weight > another.weight)
            return 1;
        else if (weight < another.weight)
            return -1;
        return 0;
    }

    @Override
    public String toString() {
        return String.format("%d-%d %.2f", v, w, weight);
    }
}

 下期有Prim实时版,时间复杂度从 ElogE 变为 ElogV

猜你喜欢

转载自www.cnblogs.com/cyy12/p/11974506.html
今日推荐