数据结构/图/最小生成树/kruskal 避圈法

kruskal 避圈法

idea:

  • 同一条路的两个端点同根则同圈
  • 使用并查集vset[]记录相连的顶点(下标和值),顶点自己和自己相连

过程

  1. 从小到达排序所有边
  2. 选择最小的边,避开圈

代码

顶点

package xcrj.kchalgorithm.graphAlgorithm.graph;

/**
 * 点
 */
public class MVertex {
    // 顶点编号,edge[][]邻接矩阵 数组下标
    private int no;
    // 顶点信息,城市A,城市B
    private char data;

    public MVertex() {
    }

    public MVertex(int no, char data) {
        this.no = no;
        this.data = data;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public char getData() {
        return data;
    }

    public void setData(char data) {
        this.data = data;
    }
}

邻接矩阵

package xcrj.kchalgorithm.graphAlgorithm.graph;

/**
 * 邻接矩阵存储图
 */
public class MGraph {
    // 顶点数量
    private int n;
    // 边数量
    private int e;
    // 顶点们
    private MVertex[] vertices;
    // 边们
    private int[][] edges;

    public MGraph() {
    }

    public MGraph(int n, int e, MVertex[] vertices, int[][] edges) {
        this.n = n;
        this.e = e;
        this.vertices = vertices;
        this.edges = edges;
    }

    public int getN() {
        return n;
    }

    public void setN(int n) {
        this.n = n;
    }

    public int getE() {
        return e;
    }

    public void setE(int e) {
        this.e = e;
    }

    public MVertex[] getVertices() {
        return vertices;
    }

    public void setVertices(MVertex[] vertices) {
        this.vertices = vertices;
    }

    public int[][] getEdges() {
        return edges;
    }

    public void setEdges(int[][] edges) {
        this.edges = edges;
    }
}

Kruskal算法

package xcrj.kchalgorithm.graphAlgorithm;

import xcrj.kchalgorithm.graphAlgorithm.graph.MGraph;
import xcrj.kchalgorithm.graphAlgorithm.graph.MVertex;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * 生成树(连通图的极小连通子图)包含图的全部顶点,用n-1条边连接起来
 * 最小生成树,边上权值之和最小的生成树
 * <p>
 * kruskal算法:避圈法
 * 1. 找最小边
 * 2. 避开圈
 */
public class MinTreeKruskal {
    static class Road {
        private int a;
        private int b;
        private int weight;

        public Road() {
        }

        public Road(int a, int b, int weight) {
            this.a = a;
            this.b = b;
            this.weight = weight;
        }

        public int getA() {
            return a;
        }

        public void setA(int a) {
            this.a = a;
        }

        public int getB() {
            return b;
        }

        public void setB(int b) {
            this.b = b;
        }

        public int getWeight() {
            return weight;
        }

        public void setWeight(int weight) {
            this.weight = weight;
        }
    }


    /**
     * 找a点的根
     *
     * @param vset 并查集,记录相连结点
     * @param a    找a点的根
     */
    public static int getRoot(int[] vset, int a) {
        // 一直找到下标和值相同则认为是根
        while (a != vset[a]) {
            a = vset[a];
        }
        return a;
    }

    /**
     * @param g     邻接矩阵
     * @param roads 路
     */
    public static int[] minTreeKruskal(MGraph g, Road[] roads) {
        // 初始化,vset[]并查集记录两个点是否相连,点自己与自己是相连的
        int[] vset = new int[g.getN()];
        // 点自己与自己相连
        for (int i = 0; i < g.getN(); i++) {
            vset[i] = i;
        }
        // 排序roads
        Arrays.sort(roads, Comparator.comparingInt(o -> o.weight));
        // 每次找最小边并且避开圈(同根则在一个圈)
        int minSumWeight = 0;
        for (int i = 0; i < g.getE(); i++) {
            // 判断一条路的端点是否在一个圈,同一条路的两端点同根则同圈
            Road road = roads[i]; // 边已经经过排序,每次取的都是最小边,只要避开圈即可
            int rootA = getRoot(vset, road.getA());
            int rootB = getRoot(vset, road.getB());
            // 同一条路的两端点不同根则不同圈则加入
            if (rootA != rootB) {
                vset[road.getA()] = road.getB();
                minSumWeight += road.getWeight();
            }
        }

        System.out.println("最小生成树权重=" + minSumWeight);
        return vset;
    }

    /**
     * @param vset 并查集,记录相连结点
     */
    public static void showMinTree(int[] vset) {
        for (int i = 0; i < vset.length; i++) {
            // 展示边的左右端点
            System.out.println(i + "-" + vset[i]);
        }
    }

    public static void main(String[] args) {
        // 构建无向带权图
        int vn = 5;
        int en = 6;
        MVertex[] mVertices = new MVertex[vn];
        for (int i = 0; i < vn; i++) {
            mVertices[i] = new MVertex(i, (char) i);
        }
        int[][] edges = new int[vn][vn];
        edges[0][4] = 6;
        edges[4][0] = 6;
        edges[0][1] = 9;
        edges[1][0] = 9;
        edges[1][2] = 3;
        edges[2][1] = 3;
        edges[0][2] = 2;
        edges[2][0] = 2;
        edges[2][3] = 5;
        edges[3][2] = 5;
        edges[3][4] = 1;
        edges[4][3] = 1;
        // 不可达认为是Integer.MAX_VALUE,比edges中所有的权值都大
        for (int i = 0; i < vn; i++) {
            for (int j = 0; j < vn; j++) {
                if (0 == edges[i][j]) edges[i][j] = Integer.MAX_VALUE;
            }
        }
        MGraph mGraph = new MGraph(vn, en, mVertices, edges);
        // 初始化Road
        Road[] roads = new Road[en];
        roads[0] = new Road(0, 1, 9);
        roads[1] = new Road(0, 2, 2);
        roads[2] = new Road(0, 4, 6);
        roads[3] = new Road(1, 2, 3);
        roads[4] = new Road(2, 3, 5);
        roads[5] = new Road(3, 4, 1);

        int[] vset = minTreeKruskal(mGraph, roads);
        showMinTree(vset);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值