算法:最小生成树(Prim算法、Kruskal算法)

 

//带权的无向邻接矩阵
public class Graph {
    ArrayList<String> vertexList;
    int[][] edgeMatrix;
    int numOfVertex;
    int numOfEdge;
    int size;

    public Graph() {
    }

    public Graph(int size) {
        vertexList = new ArrayList<String>(size);
        edgeMatrix = new int[size][size];
        //初始化邻接矩阵
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                if (x != y) {
                    edgeMatrix[x][y] = 0x3f;
                }
            }
        }
        numOfEdge = 0;
        numOfVertex = 0;
        this.size = size;
    }

    public void addVertex(String v) {
        vertexList.add(v);
        numOfVertex++;
    }

    public void addEge(int from, int to, int weight) {
        edgeMatrix[from][to] = weight;
        edgeMatrix[to][from] = weight;
        numOfEdge++;
    }

    public void showGraph() {
        for (int[] edges : edgeMatrix) {
            for (int i : edges) {
                System.out.print(i + "\t");
            }
            System.out.println();
        }
    }

    public void prim() {
        ArrayList<Integer> treeVertex = new ArrayList<>();//保存已经进入树的顶点的下标

        treeVertex.add(0);//把0这个顶点加入到树顶点的集合中
        while (true) {
            if (treeVertex.size() == vertexList.size()) {
                break;//树中已经包含了全部顶点,则退出循环
            }
            //遍历树顶点集合中的所有顶点,找到没有被访问过且权值最小的顶点
            int temp;//暂时保留当前顶点的下标
            int i = -1;//找到后,当前顶点的下标
            int j = -1;//找到后,当前顶点的最小权值对应的顶点的下标
            int min = 0x3f;//保存最小的权值,初始化为一个较大值
            for (int x = 0; x < treeVertex.size(); x++) {
                temp = treeVertex.get(x);//拿到树顶点集合中的顶点下标
                //遍历这个顶点的邻接顶点
                for (int y = 0; y < numOfVertex; y++) {
                    if (edgeMatrix[temp][y] != 0 && min > edgeMatrix[temp][y] && !treeVertex.contains(y)) {
                        min = edgeMatrix[temp][y];
                        i = temp;
                        j = y;
                    }
                }
            }
            if (min != 0x3f) {//如果找到了,k指向顶点i的邻接顶点中,权值最小顶点的下标
                treeVertex.add(j);
                System.out.println(i + "---" + j + "---" + edgeMatrix[i][j]);
            }
        }
    }

    //得到按权值排好序的边集数组
    public int[][] getEdgeArray() {
        int[][] edgeArray = new int[numOfEdge][3];
        int count = 0;
        for (int x = 0; x < numOfVertex; x++) {
            for (int y = x + 1; y < numOfVertex; y++) {
                if (edgeMatrix[x][y] != 0 && edgeMatrix[x][y] != 0x3f) {
                    edgeArray[count][0] = x;
                    edgeArray[count][1] = y;
                    edgeArray[count][2] = edgeMatrix[x][y];
                    count++;
                }
            }
        }
        //边集数组按照权值从小到大排序(冒泡)
        for (int y = 0; y < numOfEdge - 1; y++) {//大循环
            for (int x = numOfEdge - 1; x > y; x--) {//小循环,从最后一个开始
                if (edgeArray[x][2] < edgeArray[x - 1][2]) {
                    //交换数据
                    for (int i = 0; i < 3; i++) {
                        int temp = edgeArray[x - 1][i];
                        edgeArray[x - 1][i] = edgeArray[x][i];
                        edgeArray[x][i] = temp;
                    }
                }
            }
        }
        return edgeArray;
    }

    public void kruskal() {
        int[][] edgeArray = getEdgeArray();//得到边集数组
        int[] parent = new int[numOfVertex];//判断是否形成环路的数组,表示顶点i和顶点parent[i]在同一个连通分量中
        for (int x = 0; x < numOfEdge; x++) {//循环遍历每一条边
            int n = find(parent, edgeArray[x][0]);//该边的第一个顶点所在的连通分量的终点
            int m = find(parent, edgeArray[x][1]);//该边的第二个顶点所在的连通分量的终点
            if (n != m) {//这两个顶点的终点不一样,表示这两个顶点在不同的连通分量中
                parent[n] = m;//加上这条边之后,这两个顶点所在的连通分量就会接通
                System.out.println(edgeArray[x][0] + "---" + edgeArray[x][1] + "---" + edgeArray[x][2]);
            }
        }
    }

    //得到以i为起点的连通分量的终点(若形成回路,或只有一个顶点,终点为i本身)
    public int find(int[] parent, int i) {
        while (parent[i] != 0) {
            i = parent[i];
        }
        return i;
    }

    public void printArray(int[][] edgeArray) {
        for (int[] edges : edgeArray) {
            for (int i : edges) {
                System.out.print(i + "\t");
            }
            System.out.println();
        }
    }
}


public class GraphDemo {
    public static void main(String[] args) {
        Graph graph = new Graph(9);

        //添加顶点
        graph.addVertex("v0");
        graph.addVertex("v1");
        graph.addVertex("v2");
        graph.addVertex("v3");
        graph.addVertex("v4");
        graph.addVertex("v5");
        graph.addVertex("v6");
        graph.addVertex("v7");
        graph.addVertex("v8");

        //添加边
        graph.addEge(0, 1, 10);
        graph.addEge(0, 5, 11);
        graph.addEge(1, 6, 16);
        graph.addEge(5, 6, 17);
        graph.addEge(1, 8, 12);
        graph.addEge(1, 2, 18);
        graph.addEge(2, 8, 8);
        graph.addEge(2, 3, 22);
        graph.addEge(3, 8, 21);
        graph.addEge(3, 6, 24);
        graph.addEge(6, 7, 19);
        graph.addEge(3, 7, 16);
        graph.addEge(3, 4, 20);
        graph.addEge(4, 5, 26);
        graph.addEge(4, 7, 7);

        graph.showGraph();//邻接矩阵
        graph.prim();//普利姆算法
        
        graph.printArray(graph.getEdgeArray());//边集数组
        graph.kruskal();//克鲁斯卡尔算法

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值