用Java实现Prim算法和Kruskal算法

最近刷题刷到要用到最小生成树的题,索性就对Prim算法和Kruskal算法做一个总结。

图的样例如下所示:
在这里插入图片描述

Prim算法:

算法思路:
1.初始条件:V={1,2,3,4,5,6},E为一个邻接矩阵。
2.首先创建两个集合vNew={},vOld=V。
3.接下来重复下列a,b两个操作,直到vNew = V:
   a.在集合E中选取权值最小的边<u, v>,其中u为集合vNew中的元素,v为集合vOld中的元素。
   b.将v加入到集合vNew中,将vOld中的v去除。
4.打印。

代码实现:

public class Prim {
    public static void main(String[] args) {
        float m = Float.MAX_VALUE;
        float[][] weight = {
                {0, 0, 0, 0, 0, 0, 0},
                {0, m, 6, 1, 5, m, m},
                {0, 6, m, 5, m, 3, m},
                {0, 1, 5, m, 5, 6, 4},
                {0, 5, m, 5, m, m, 2},
                {0, m, 3, 6, m, m, 6},
                {0, m, m, 4, 2, 6, m}};

        prim(weight);
    }

    public static void prim(float[][] weight){
        int vNum = weight.length - 1;
        List<Integer> newList = new ArrayList<>(); //新集合存放已经找到的点
        List<Integer> oldList = new ArrayList<>(); //旧集合存放还未找到的点

        for(int i=1;i<=6;i++){
            oldList.add(i);
        }
        newList.add(1);
        oldList.remove(new Integer(1));
        while (newList.size() != vNum) {
            float min = Float.MAX_VALUE;
            int oldIndex = 1; //找到最小边时旧集合的点
            int newIndex = 1; //找到最小边时新集合的点
            for (int i = 0; i < newList.size(); i++) {  //寻找新集合到旧集合的最小权值的边
                int index = newList.get(i);
                for (int j = 0; j <oldList.size(); j++) {
                    int k = oldList.get(j);
                    if (min > weight[index][k]) { //记录最小边的相关信息,用于打印
                        min = weight[index][k];
                        oldIndex = k;
                        newIndex = index;
                    }
                }
            }
            System.out.println(newIndex + "->" + oldIndex + ": " + min);
            weight[newIndex][oldIndex] = Float.MAX_VALUE; //找过的最小边置为最大值
            weight[oldIndex][newIndex] = Float.MAX_VALUE; //对称边也要置为最大值
            newList.add(oldIndex);
            oldList.remove(new Integer(oldIndex));
        }
    }
}

注意: 这里有个坑,一开始我在代码最后写的是oldList.remove(oldIndex),但是发现删除的是oldIndex索引对应的值,而不是oldIndex对象。所以要改为oldList.remove(new Integer(oldIndex))。

打印结果如下图所示:
在这里插入图片描述

Kruskal算法

算法思路:
按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。

Kruskal算法的思想很简单,但是实现起来如何保证不构成回路是一个难点,但是我在网上找到一个很巧妙的方法,代码实现如下:

public class Kruskal {
    public static void main(String[] args) {
        EdgeT[] edges = new EdgeT[10];
        edges[0] = new EdgeT(1, 2, 6);
        edges[1] = new EdgeT(1, 3, 1);
        edges[2] = new EdgeT(1, 4, 5);
        edges[3] = new EdgeT(2, 3, 5);
        edges[4] = new EdgeT(2, 5, 3);
        edges[5] = new EdgeT(3, 4, 5);
        edges[6] = new EdgeT(3, 5, 6);
        edges[7] = new EdgeT(3, 6, 4);
        edges[8] = new EdgeT(4, 6, 2);
        edges[9] = new EdgeT(5, 6, 6);
        kruskal(edges);
    }

    public static void kruskal(EdgeT[] edges) {
        Arrays.sort(edges, (o1,o2)->o1.weight-o2.weight);

        // 定义一个一维数组,下标为边的起点,值为边的终点
        int[] temp = new int[edges.length+1];

        for (EdgeT edge : edges) {
            // 找到起点和终点在临时边数组中的最后连接点
            int start = find(temp, edge.start);
            int end = find(temp, edge.end);

            // 通过起点和终点找到的最后连接点是否为同一个点,是则产生回环
            if (start != end) {
                // 没有产生回环则将临时数组中,起点为下标,终点为值
                temp[start] = end;
                System.out.println(edge.start + "->" + edge.end + ":" + edge.weight);
            }
        }
    }

    public static int find(int temp[], int index) {
        while (temp[index] > 0) {
            index = temp[index];
        }
        return index;
    }
}

class EdgeT {
    int start;
    int end;
    int weight;

    public EdgeT(int start, int end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }
}

打印结果如下图所示:
在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值