package kruscal;

import java.util.Arrays;

public class KruscalCase {

    private int edgeNum; //边的个数
    private char[] vertex;  //顶点数组
    private int[][] matrix; //邻接矩阵
    //设置INF,表示两个节点不能连通
    private static final int INF=Integer.MAX_VALUE;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        char[] vertex= {'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}
        };

        //创建KruskcalCase实例对象
        KruscalCase kruscalCase=new KruscalCase(vertex,matrix);
        //输出构建的
        kruscalCase.print();
        kruscalCase.kruskal();

    }

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

        //初始化顶点,this.vertex=vertex;
        //这里采用复制的方式
        this.vertex=new char[vlen];
        for(int i=0;i<vertex.length;i++) {
            this.vertex[i]=vertex[i];
        }

        //初始化边,可采用this.matrix=matrix;
        //这里同样采用复制
        this.matrix=new int[vlen][vlen];
        for(int i=0;i<vertex.length;i++) {
            for(int j=0;j<vertex.length;j++) {
                this.matrix[i][j]=matrix[i][j];
            }
        }

        //统计边的条数
        for(int i=0;i<vlen;i++) {
            for(int j=i+1;j<vlen;j++) {
                if(this.matrix[i][j]!=INF) {
                    edgeNum++;
                }
            }
        }
    }

    public void kruskal() {
        int index=0;  //表示最后结果数组的索引
        int[] ends=new int[edgeNum]; //表示已生成的"最小生成树"中的每个顶点在最小生成树中的终点
        //创建结果数组,保存最后的最小生成树
        EData[] rets=new EData[edgeNum];

        //获取图中所有的边的集合
        EData[] edges=getEdges();
        System.out.println("图的边的集合="+Arrays.toString(edges)+"共"+edges.length);

        //按照边的权值大小从小到大排序
        sortEdges(edges);

        //遍历edges数组,将边添加到最小生成树中时,判断准备加入的边是否形成了回路,如果没有,就加入rets,否则不能加入
        for(int i=0;i<edgeNum;i++) {
            //获取第i条边的第一个顶点
            int p1=getPosition(edges[i].start);
            //获取第i条边的第二个顶点
            int p2=getPosition(edges[i].end);

            //获取p1顶点在已有最小生成树的终点
            int m=getEnd(ends,p1);
            //获取p2顶点在已有最小生成树的终点
            int n=getEnd(ends,p2);

            //是否构成回路
            if(m!=n) {
                ends[m]=n;
                rets[index++]=edges[i];
            }
        }

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

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

    /**
     * 功能:对边进行排序处理,冒泡排序
     * @param edges 边的集合
     */
    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].weigh>edges[j+1].weigh){
                    EData temp=edges[j];
                    edges[j]=edges[j+1];
                    edges[j+1]=temp;
                    }
                }
        }
    }

    /**
     * 
     * @param ch 顶点的值,比如'A','B'
     * @return 返回ch顶点的下标,如果找不到就返回-1
     */
    private int getPosition(char ch) {
        for(int i=0;i<vertex.length;i++) {
            if(vertex[i]==ch) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 功能:获取图中的边,放到EData[]数组中,后面我们需要遍历数组
     * 通过matrix邻接矩阵获取
     * EData形式:[['A','B',12],['B','F',7],....]
     * @return 
     */
    private EData[] getEdges() {
        int index=0;
        EData[] edges=new EData[edgeNum];
        for(int i=0;i<vertex.length;i++) {
            for(int j=i+1;j<vertex.length;j++) {
                if(matrix[i][j]!=INF) {
                    edges[index++]=new EData(vertex[i],vertex[j],matrix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * 功能:获取下标为i的顶点的终点,用于后面判断两个顶点的终点是否相同
     * @param ends 数组记录就是遍历过程中每次各个顶点的终点,动态的
     * @param i 表示传入顶点的对应下标
     * @return 返回的是终点对应的下标
     */
    private int getEnd(int[] ends,int i) {
        while(ends[i]!=0) {
            i=ends[i];
        }
        return i;
    }
}

class EData{
    char start;
    char end;
    int weigh;
    //构造器
    public EData(char start,char end,int weigh) {
        this.start=start;
        this.end=end;
        this.weigh=weigh;
    }

    @Override
    public String toString() {
        return "EData [<" + start + "," +end+ ">=" + weigh + "]";
    }

}