kruskal 避圈法
idea:
- 同一条路的两个端点同根则同圈
- 使用并查集vset[]记录相连的顶点(下标和值),顶点自己和自己相连
过程
- 从小到达排序所有边
- 选择最小的边,避开圈
代码
顶点
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);
}
}