贪心算法实例(七):最小生成树Kruskal

     什么是最小生成树?

    生成树是相对图来说的,一个图的生成树是一个树并把图的所有顶点连接在一起。一个图可以有许多不同的生成树。一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树其实是最小权重生成树的简称。生成树的权重是考虑到了生成树的每条边的权重的总和。

    最小生成树有几条边?

    最小生成树有(V – 1)条边,其中V是给定的图的顶点数量。

    Kruskal算法

   下面是步骤寻找MST使用Kruskal算法:

    1,按照所有边的权重排序(从小到大)

    2,选择最小的边。检查它是否形成与当前生成树形成环。如果没有形成环,讲这条边加入生成树。否则,丢弃它。 

    3,重复第2步,直到有生成树(V-1)条边

具体实例及实现代码如下所示:

/**  * @Title: Kruskal.java  * @Package greedyalgorithm  * @Description: TODO  * @author peidong  * @date 2017-6-1 上午9:34:10  * @version V1.0  */ package greedyalgorithm;

import java.util.Collections;
import java.util.LinkedList;

/**  * @ClassName: Kruskal  * @Description: 最小生成树Kruskal算法实现  * @date 2017-6-1 上午9:34:10  *  */  public class Kruskal {

    /**  *  * @ClassName: FastUnionFind  * @Description: 由连通分支组成的集合为U,包括union(a,b);和find(v)的基本运算  * @date 2017-6-1 上午9:50:11  *  */  public static class FastUnionFind {
        public int[] u;  //数组用来保存顶点所属的集合,用数字表示
        public FastUnionFind(int n){
            u = new int[n + 1];
            for(int i = 1; i <= n; i++){ //初始化顶点数组值
                u[i] = i;
            }
        }
        public int find(int x){ //找到顶点所属的集合
            return u[x];
        }

        public void union(int x, int y){ //将第二个顶点归入第一个顶点集合
            u[y] = u[x];
        }
    }

    public static class EdgeNode implements Comparable{
        float weight; //边权值
        int  u, v; //边的左右顶点

        /**  *  * <p>Title: </p>  * <p>Description:构造函数 </p>  * @param u  * @param v  * @param weight  */  public EdgeNode(int u, int v, float weight){
            this.u = u;
            this.v = v;
            this.weight = weight;
        }

        /**  * 按照边的权值升序排列  */  public int compareTo(Object x){
            float xw = ((EdgeNode)x).weight;
            if(weight < xw)
                return -1;
            if(weight == xw)
                return 0;
            return 1;
        }
    }

    /**  *  * @Title: kruskal  * @Description: kruskal算法  * @param n 所有顶点的数目  * @param E 边的集合(所有的边)  * @param t 保存逐步连通的边  * @return 是否生成了最小生成树  * @return boolean  * @throws  */  public static boolean kruskal(int n, LinkedList<EdgeNode> E, EdgeNode[] t){
        FastUnionFind U = new FastUnionFind(n);
        int k = 0;
        while(k < n-1){  //n个顶点,n-1条边
            EdgeNode x = E.peek();
            int a = U.find(x.u);  //边的左顶点所属的集合
            int b = U.find(x.v);  //边的右顶点所属集合
            if(a != b){
                t[k++] = x;
                U.union(a, b);
            }
            E.pop();
        }
        for(int i = 0; i < k; i++){
            System.out.println("左顶点:"+t[i].u+"; 右顶点:"+t[i].v+"; 长度:"+t[i].weight);
        }
        return (k == n-1);
    }
    /**  * @Title: main  * @Description: TODO  * @param args  * @return void  * @throws  */  public static void main(String[] args) {
        // TODO Auto-generated method stub  int n=6;
        EdgeNode e1=new EdgeNode(1,2,6);
        EdgeNode e2=new EdgeNode(1,3,1);
        EdgeNode e3=new EdgeNode(1,4,5);
        EdgeNode e4=new EdgeNode(2,3,5);
        EdgeNode e5=new EdgeNode(3,4,5);
        EdgeNode e6=new EdgeNode(2,5,3);
        EdgeNode e7=new EdgeNode(3,5,6);
        EdgeNode e8=new EdgeNode(5,6,6);
        EdgeNode e9=new EdgeNode(3,6,4);
        EdgeNode e10=new EdgeNode(4,6,2);
        LinkedList<EdgeNode> E=new LinkedList<EdgeNode>();
        E.add(e10);
        E.add(e9);
        E.add(e8);
        E.add(e7);
        E.add(e6);
        E.add(e5);
        E.add(e4);
        E.add(e3);
        E.add(e2);
        E.add(e1);
        Collections.sort(E);
        EdgeNode[] t=new EdgeNode[n];
        kruskal(n,E,t);
    }
}

//时间复杂度:
//
//O(ElogE) 或 O(ElogV)。 排序使用 O(ELogE) 的时间,之后我们遍历中使用并查集O(LogV) ,所以总共复杂度是 O(ELogE + ELogV)。E的值最多为V^2,所以
//
//O(LogV) 和 O(LogE) 可以看做是一样的。

    原文作者:贪心算法
    原文地址: https://blog.csdn.net/u012845311/article/details/73369020
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞