[算法] 克鲁卡尔算法 最小生成树

package com.guigu.algorithm.kruskal;

import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode;

import java.util.Arrays;

/**
 * @author: guorui fu
 * @versiion: 1.0
 * 克鲁斯卡算法
 */
public class KruskalCase {

    private int edgeNum;//边的个数
    private char[] vertexes;//顶点数组
    private int[][] matrix;//邻接矩阵
    private static final int INF = Integer.MAX_VALUE;//用来表示顶点不能连通

    public static void main(String[] args) {
        char[] vertexes = {'A','B','C','D','E','F','G'};
        int matrix[][] = {
                {0,12,INF,INF,INF,16,14},
                {12,0,10,INF,INF,7,INF},
                {INF,10,0,3,5,6,INF},
                {INF,INF,3,0,4,INF,INF},
                {INF,INF,5,4,0,2,8},
                {16,7,6,INF,2,0,9},
                {14,INF,INF,INF,8,9,0},
        };
        //创建对象实例
        KruskalCase kruskalCase = new KruskalCase(vertexes, matrix);
        kruskalCase.print();

        //拿到所有图中所有边
//        EData[] edges = kruskalCase.getEdges();
//        System.out.println("排序前" + Arrays.toString(edges));
//        kruskalCase.sortEdges(edges);
//        System.out.println("排序后" + Arrays.toString(edges));
        kruskalCase.kruskal();
    }

    //构造器
    public KruskalCase(char[] vertexes,int[][] matrix){
        int vlen = vertexes.length;//顶点数

        //初始化顶点。复制拷贝
        this.vertexes = new char[vlen];
        for (int i = 0; i < vertexes.length; i++) {
            this.vertexes[i] = vertexes[i];
        }
//        this.vertexes = vertexes;

        //初始化边 这次直接克隆 或者可以双层循环进行复制拷贝
        this.matrix = matrix.clone();
        //统计边
        for (int i = 0; i < vlen; i++) {
            for (int j = 0; j < vlen; j++) {
                if (this.matrix[i][j] != INF && this.matrix[i][j] != 0){
                    edgeNum++;
                }
            }
        }
    }

    //
    public void kruskal(){
        int index = 0;//表示最后结果数组的索引
        //保存已有最小生成树的每个顶点的终点 也就是遍历到相同顶点时终点只能是之前保存过顶点的终点
        int[] ends = new int[edgeNum];
        //创建EData数组,保存最小生成树结果
        EData[] rets = new EData[edgeNum];

        //获取图 所有边的集合,一共有12条
        EData[] edges = getEdges();
        //按变得权值进行从小到大的排序 从权值小到大遍历
        sortEdges(edges);

        //遍历edges 判断是否构成回路
        for (int i = 0;i < edgeNum/2;i++){//已经排序过,从数组中最小权值的边开始
            //获取到第i条边的第一个顶 和 第二个顶点
            int p1 = getPosition(edges[i].start);
            int p2 = getPosition(edges[i].end);

            //获取p1 p2顶点在已有的最小生成树的终点是
            int e1 = getEnd(ends, p1);
            int e2 = getEnd(ends, p2);
            //是否构成回路 也就是终点不能相同才可以加入最小树
            if (e1 != e2){//ends[e1] 下标e1代表边起点 值e2代表边终点
                ends[e1] = e2;//e1的终点是e2
                rets[index++] = edges[i];//有一条边加入rets数组
            }
        }
        //打印最小生成树 输出rets
        System.out.println("最小生成树为=" + Arrays.toString(rets));
    }

    //打印邻接矩阵
    public void print(){
        System.out.println("邻接矩阵为:");
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.printf("%12d",matrix[i][j]);
            }
            System.out.println();
        }
    }

    //对边的集合按权值大小进行排序处理 冒泡排序
    //完成排序后 就可以从权值最小的顶点出发 进行连接
    private void sortEdges(EData[] edges){
        for (int i = 0; i < edges.length - 1; i++) {
            for (int j = 0; j < edges.length - 1 - i; j++) {
                if (edges[j].weight > edges[j + 1].weight){//交换
                    EData temp = edges[j];
                    edges[j] = edges[j + 1];
                    edges[j+1] = temp;
                }
            }
        }
    }

    //传入顶点值,返回下标
    private int getPosition(char ch){
        for (int i = 0; i < vertexes.length; i++) {
            if (ch == vertexes[i]){
                return i;
            }
        }
        return -1;
    }

    //获取图中的边 放入EData[]数组中,之后需要遍历该数组
    private EData[] getEdges(){
        int index = 0;
        EData[] edges = new EData[edgeNum/2];
        for (int i = 0; i < vertexes.length; i++) {//斜对称 坐标ij与ji边相同,所以遍历其中一半部分就行
            for (int j = i + 1; j < vertexes.length; j++) {
                if (matrix[i][j] != INF){
                    //保存边的起点 终点 权值
                    edges[index++] = new EData(vertexes[i],vertexes[j], matrix[i][j]);
                }
            }
        }
        //返回所有边的信息集合:起点 终点 权值
        return edges;
    }

    //获取下标为i的顶点的终点,用于判断两个顶点的终点是否相同
    //ends[] 是在遍历过程中逐步形成 i表示传入的顶点对应的下标
    private int getEnd(int[] ends,int i){
        while (ends[i] != 0){//循环找到终点 例如ends[1]=3 -> ends[3]=6 ->ends[6]=0 则返回终点6
            i = ends[i];
        }
        return i;
    }
}

//创建一个类EData,它的对象实例就表示一条边
class EData{
    char start;//起点
    char end;//终点
    int weight;//边的权值

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

    @Override
    public String toString() {
        return "EData{" +
                "start=" + start +
                ", end=" + end +
                ", weight=" + weight +
                '}';
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值