数据结构和算法学习之克鲁斯卡尔(kruskal)算法

package com.atguigu.kruskal;


import java.util.Arrays;

/**
 * @author 
 * @create 2022-08-19-16:07
 */
public class KruskalAlgorithm {
    private int edgeNum;//记录边的个数
    private char[] vertexs;//顶点数组
    private int[][] matrix;//邻接矩阵
    //使用INF表示两个顶点不能联通
    private static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = {
                /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
                /*A*/ {0, 12, INF, INF, INF, 16, 14},
                /*B*/ {12, 0, 10, INF, INF, 7, INF},
                /*C*/ {INF, 10, 0, 3, 5, 6, INF},
                /*D*/ {INF, INF, 3, 0, 4, INF, INF},
                /*E*/ {INF, INF, 5, 4, 0, 2, 8},
                /*F*/ {16, 7, 6, INF, 2, 0, 9},
                /*G*/ {14, INF, INF, INF, 8, 9, 0}};

        KruskalAlgorithm kruskalAlgorithm = new KruskalAlgorithm(vertexs, matrix);
        //输出构建的图
        kruskalAlgorithm.printMatrix();
        EData[] edges = kruskalAlgorithm.getEdges();
        //未排序前
        System.out.println("排序前" + Arrays.toString(edges));
        //排序
        kruskalAlgorithm.sortEdge(edges);
        System.out.println("排序后" + Arrays.toString(edges));

        System.out.println("================");
        kruskalAlgorithm.kruskal();
    }

    //构造器
    public KruskalAlgorithm(char[] vertexs, int[][] matrix) {
        //初始化顶点数和边的个数
        int vlen = vertexs.length;

        //初始化顶点,复制拷贝的方式
        this.vertexs = new char[vlen];
        for (int i = 0; i < vertexs.length; i++) {
            this.vertexs[i] = vertexs[i];
        }
//        //或者可以用下面方式
//        this.vertexs = vertexs;

        //初始化边
        this.matrix = new int[vlen][vlen];
        //采用拷贝的方式赋值
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                this.matrix[i][j] = matrix[i][j];
            }
        }
        //统计边的数量
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (this.matrix[i][j] != INF && this.matrix[i][j] != 0) {
                    edgeNum++;
                }
            }
        }


    }

    //打印邻接矩阵
    public void printMatrix() {
        System.out.println("打印邻接矩阵");
        //方法1:
//        for (int[] line : this.matrix) {
//            System.out.println(Arrays.toString(line));
//
//        }

        //方法2:
        for (int i = 0; i < this.matrix.length; i++) {
            for (int j = 0; j < this.matrix[0].length; j++) {
                System.out.printf("%13d", this.matrix[i][j]);

            }
            System.out.println();
        }
    }

    //对边进行排序

    /**
     * @param edges 边的集合
     */
    private void sortEdge(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;

                }
            }
        }

    }

    //返回顶点对应的下标

    /**
     * @param ch 顶点,如‘A’,'B'
     * @return 返回顶点在vertexs数组中的下标,若不存在,则返回-1
     */
    private int getPosition(char ch) {
        for (int i = 0; i < vertexs.length; i++) {
            if (vertexs[i] == ch) {
                return i;
            }
        }
        //找不到返回-1
        return -1;
    }

    /**
     * 功能:将边对象作为EData[]数组的一个元素存放起来
     * 通过邻接矩阵matrix来获取
     * 如:[['B','F',12],['A','B',3],...]
     *
     * @return
     */
    private EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] != INF && matrix[i][j] != 0) {
                    edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);

                }
            }
        }
        return edges;
    }

    /**
     * @param ends ends[i]记录了每个顶点对应终点是哪个,ends数组在遍历过程中逐步形成
     * @param i    传入顶点对应的下标
     * @return 返回下标为i的顶点的终点
     * 用于后续判断两个顶点终点是否相同
     */
    private int getEnds(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

    //克鲁斯卡尔算法
    public void kruskal() {
        int index = 0;//用于最后结果的数组索引
        int[] ends = new int[edgeNum];//用于保存在已有最小生成树中每个顶点的终点
        //用于存放最后结果
        EData[] results = new EData[edgeNum];

        EData[] edges = getEdges();//拿到图中所有的边 12条
        //排序
        sortEdge(edges);

        //遍历edges数组,通过判断边来判断该边是否加入到结果数组中
        for (int i = 0; i < edges.length; i++) {
            //得到第i个边的第一个顶点的下标
            int p1 = getPosition(edges[i].start);
            //得到第i个边的第二个顶点的下标
            int p2 = getPosition(edges[i].end);


            //得到第i个边的第一个顶点的终点
            int m = getEnds(ends, p1);
            //得到第i个边的第二个顶点的终点
            int n = getEnds(ends, p2);
            //判断是否其终点相同
            if (m != n) {//如果重点不相同则将该边加入到结果数组中
                ends[m] = n;//设置m在已有生成树的重点为n
//                ends[n]=n;//这句没有必要写,在getEnds()方法中就已经实现了这个要求
                results[index++] = edges[i];//有一条边加入数组中

            }

        }

        //统计并打印最小生成树,输出result数组
        System.out.println("最小生成树为=");
        for (int i = 0; i < index; i++) {
            System.out.println(results[i]);
        }


    }



}

//创建边类,实例表示一条边
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;

    }

    //为了方便后续输出边的信息,重写toString方法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值