prim算法与kruskal算法java实现,简单分析其区别

prim算法:

就我自己的通俗理解,一个图有n个顶点,我们随便找一个点作为最小树的顶点,加入树中,我们要找下一个点加入树中,我们找的这个点要求,1.与现在的最小树有相连的边(就是),2.该边还是所有与现在树相连的边中最小的,3.该点没在最小树中。满足就加入到最小树中。直到所有点都加入到树中,最小树就生成了。我画个图可能更好理解。

 

这里

 现在有这几个点,假设我先将a加入最小树

 现在最小树只有一个点,我们要找下一个满足条件的点加入最小树中,直到所有点都加入到最小树中。

 

 

 最小的是a,b就把b加入,现在最小树

 然后在从还没有加入最小树的点集合里找,里已经在最小树集合最近的点,

 所以现在最小树变成

 

 这里有两条一样小且满足条件的边,我们就随便选择一条。

 为什么不去比较ae,和cf这两条边呢,因为我们的边一头是已经在最小树的点,一边是还没加入最小树的点,而此时a,e,c,f都已经在最小树中了

 

 

 

 此时最小树已经生成,当所有点都加入到树中。

 上代码:

 

//tu是图的加权临界矩阵
public static void prim(int[][] tu){
        int[] vertix = new int[tu.length];//用来记录该点是否已经加入树中
        ArrayList<Integer> ints = new ArrayList<Integer>();//用来记录已经加入树中的点
        vertix[0] = 1;//表示0这个点已经加入树中了
        ints.add(0);//记录0这个点加入树中
//结束条件所有点都加入了树中就结束
        while (ints.size() < tu.length) {
            int minDistance = Integer.MAX_VALUE;    //用于寻找最小权值,初始化为int最大值,相当于无穷大的意思
            int minV = -1;   //用于存放未被放入树中顶点中与已被遍历顶点有最小权值的顶点
            int minI = -1;   //用于存放已被放入树中的顶点与未被遍历顶点有最小权值的顶点  ;即tu[minI][minV]在剩余的权值中最小,就是我们要找的边
            for(int i = 0;i < ints.size();i++) {   //i 表示已被加入树中的顶点
                int v1 = ints.get(i);
                for(int j = 0;j < tu.length;j++) {
                    if(vertix[j] != 1) {    //满足此条件的表示,顶点j未被加入树中
                        if(tu[v1][j] != -1 && tu[v1][j] < minDistance) {//tu[v1][j]值为-1表示v1和j是非相邻顶点
                            minDistance = tu[v1][j];
                            minV = j;
                            minI = v1;
                        }
                    }
                }
            }
            vertix[minV] = 1;
            ints.add(minV);
            System.out.println("("+minI+"->"+minV+")"+"权重:"+tu[minI][minV]);
            tu[minI][minV] = 0;
            tu[minV][minI] = 0;

        }

        
        System.out.println();
        for (int[] ints1 : tu) {
            for (int i : ints1) {
                System.out.print(i+",");
            }
            System.out.println();
        }
    }

 kruskal算法:

简单描述,就是将所有边按权重从小到大排序,然后就是从最小的边开始选,是否选这条边加入的条件是看如果这条边加入后是否会形成环,如果会形成环就跳过这条边,如果不会就加入。如果有n个顶点,就直到找到n-1条边就结束。

 

第一次肯定加入bc,我们怎么来判断加入这条边是否会形成环呢?

判断是否成环:

我直接说如何实现:

假设有5个点,现在他们就是五个树,他们的根顶点就是自己。用一个数组来装他们各自根顶点,现在bc变成一个树,那么就把以c为根顶点的所有点的根顶点变成b的根顶点。

开始数组{0,1,2,3,4},现在{0,1,1,3,4}。我们怎么判断这个边加入后是否会成环,如果边是ad,a点的根顶点与d的根顶点相同就表示他们在一个树上,那么ad加入就会形成环,如果不相等就不会形成环,就可以加入。

1. 

 2.

3. 

4. 

 本来该选4的,最小,但是如果加入就会形成环,所以就跳过这条边,选这下一条边。

 此时最小树就生成了。5个点,4条边。

 上代码:

 

class ENode {
    int start; // 边的起点
    int end; // 边的终点
    int weight; // 边的权重

    public ENode(int start, int end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }
}
//将邻接矩阵转换成对应的边
class Graph {
    private static final int INF = -1; // 表示没有边

    int[] vertexs; // 顶点集合
    int[][] matrix; // 邻接矩阵

    // 得到当前有向图中的所有边信息
    public List<ENode> getEdges() {
        List<ENode> edges = new ArrayList<ENode>();
        for (int i = 0; i < vertexs.length; i++) {
//生成的边都是ab这样的,就是始点比终点小,就是代号更小,我这里存的是0,1,2,3,4....
            for (int j = i; j < vertexs.length; j++) {
                if (matrix[i][j] != INF) {
                    ENode edge = new ENode(vertexs[i], vertexs[j], matrix[i][j]);
                    edges.add(edge);
                }
            }
        }

        return edges;
    }
}
public static void qSort(List<ENode> edges, int low, int high) {
        if (low < high) {
            int i = low, j = high;
            ENode edge = edges.get(low);
            while (i < j) {
                while (edge.weight <= edges.get(j).weight && i < j) {
                    j--;
                }
                edges.set(i, edges.get(j));
                while (edge.weight >= edges.get(i).weight && i < j) {
                    i++;
                }
                edges.set(j, edges.get(i));
            }
            edges.set(i, edge);
            qSort(edges, low, i - 1);
            qSort(edges, i + 1, high);
        }
    }

    public static void kruskal(Graph graph){
        List<ENode> edges = graph.getEdges();
        int edgeNum = edges.size();

        // 2.对所有有向边进行排序
        qSort(edges, 0, edgeNum - 1);
        int[] vertexs = graph.vertexs;
        int length = graph.vertexs.length;
        for (int i = 0; i < length-1; i++) {
            for (int i1 = 0; i1 < edges.size(); i1++) {
                ENode eNode = edges.get(i1);
                if (vertexs[eNode.start] != vertexs[eNode.end]) {
                    System.out.println(zm[eNode.start]+"->"+zm[eNode.end]+"权重:"+eNode.weight);
                    int start = vertexs[eNode.start];
                    int end = vertexs[eNode.end];
                    for (int i2 = 0; i2 < vertexs.length; i2++) {
                        if (vertexs[i2] == end) {
                            vertexs[i2] = start;
                        }
                    }
                }
            }

        }

    }
public static void test2(){
        int[][] array1 = {{-1,3,5,4,-1,-1,-1,-1,-1,-1,-1,-1},
                {3,-1,-1,-1,3,6,-1,-1,-1,-1,-1,-1},
                {5,-1,-1,2,-1,-1,4,-1,-1,-1,-1,-1},
                {4,-1,2,-1,1,-1,-1,5,-1,-1,-1,-1},
                {-1,3,-1,1,-1,2,-1,-1,4,-1,-1,-1},
                {-1,6,-1,-1,2,-1,-1,-1,-1,5,-1,-1},
                {-1,-1,4,-1,-1,-1,-1,3,-1,-1,6,-1},
                {-1,-1,-1,5,-1,-1,3,-1,6,-1,7,-1},
                {-1,-1,-1,-1,4,-1,-1,6,-1,3,-1,5},
                {-1,-1,-1,-1,-1,5,-1,-1,3,-1,-1,9},
                {-1,-1,-1,-1,-1,-1,6,7,-1,-1,-1,8},
                {-1,-1,-1,-1,-1,-1,-1,-1,5,9,8,-1}};
        int[][] array={{-1,5,-1,6,-1},
                {5,-1,1,3,-1},
                {-1,1,-1,4,6},
                {6,3,4,-1,2},
                {-1,-1,6,2,-1}
        };
        int[] v = new int[array.length];
        for (int i = 0; i < v.length; i++) {
            v[i]=i;
        }
        Graph graph = new Graph();
        graph.matrix= array;
        graph.vertexs = v;
        kruskal(graph);
    }

 prim算法与kruskal算法区别

 

对于最小生成树,有两种算法可以解决。一种是Prim算法,该算法的时间复杂度为O(n²),与图中边数无关,该算法适合于稠密图,而另外一种是Kruskal,该算法的时间主要取决于边数,它较适合于稀疏图。

Prim算法:设图G =(V,E),其生成树的顶点集合为U。 ①、把v0放入U。 ②、在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。 ③、把②找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行②。

Kruskal算法与Prim算法的不同之处在于,Kruskal在找最小生成树结点之前,需要对所有权重边做从小到大排序。将排序好的权重边依次加入到最小生成树中,如果加入时产生回路就跳过这条边,加入下一条边。当所有结点都加入到最小生成树中之后,就找出了最小生成树。

 

1.Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。

  2.与Dijkstra类似,Prim算法也可以用堆优化,优先队列代替堆,优化的Prim算法时间复杂度O(mlogn)模板,Prim_heap在任何时候都有令人满意的的时间复杂度,但是代价是空间消耗极大。(以及代码很复杂>_<)

  但值得说一下的是,时间复杂度并不能反映出一个算法的实际优劣。

  竞赛题一般给的都是稀疏图,选择Prim_heap即可;如果觉得代码量太大,想要在Prim与Kruskal算法中选一个,那就选择Kruskal算法。

 

 借鉴:图论——最小生成树:Prim算法及优化、Kruskal算法,及时间复杂度比较 - 嘤嘤狂吠OVO - 博客园

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值