携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
最小生成树是什么
对于一个带权连通无向图G=(V,E),生成树不同,每棵树的权(树中所有边上的权值和)也不同,设R为G的所有生成树的集合,若T为R中权值和最小的生成树,则T称为G的最小生成树(Minimum-Spanning-Tree,MST)
特点:
- 最小生成树可能有多个,但边的权值之和总是唯一且最小的
- 最小生成树的边数=定点数-1,砍掉一条则不连通,增加一条则会出现回路
- 若一个连通图本身就是一颗树,则其最小生成树就是它本身
- 只有连通图才有生成树,非连通图只有生成森林
常见的最小生成树的算法:
- 普里姆算法
- Kruskal算法(克鲁斯卡尔)
普里姆算法
从某一个顶点(所以存在多个最小生成树)开始构建生成树,每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。类似贪心算法
Kruskal算法(克鲁斯卡尔)
每次选择权值最小的边,使这条边的两头连通(原本已近连通则不选)直到所有结点都连通
代码实现
public class Kruskal {
public int verticeSize;
public int[] vertices;
public int[][] matrix;
public int edgeSize;
public static final int MAX_WEIGHT = 0xFFF8;
public Edge[] edges;
public Kruskal(int verticeSize) {
this.verticeSize = verticeSize;
matrix = new int[verticeSize][verticeSize];
}
public static class Edge{
int start;
int end;
int weight;
public Edge(int start, int end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
private Edge[] getEdges() {
int index = 0;
Edge[] edges = new Edge[verticeSize * verticeSize];
for(int i = 0;i < verticeSize; i++) {
for(int j = 0; j < verticeSize;j++) {
if (matrix[i][j] != 0 && matrix[i][j] != MAX_WEIGHT) {
edges[index++] = new Edge(i, j, matrix[i][j]);
}
}
}
edgeSize = index;
return edges;
}
private void sortEdge(Edge[] cur_edge, int size) {
for(int i = 0; i < size; i++) {
for (int j = i+1; j < size; j++) {
if (edges[i].weight > edges[j].weight) {
Edge tmp = edges[i];
edges[i] = edges[j];
edges[j] = tmp;
}
}
}
}
/**
* 核心算法
*/
public void kruskal(){
edges=getEdges();
int index=0;
Edge[] cur_edge=edges;//存放排序后的边数组
Edge[] rets=new Edge[edgeSize];//用来存放结果
sortEdge(cur_edge,edgeSize);
//定义一个数组,用来存放连通分量,用来表示连通关系的
//下标用来表示起点,值表示终点
int[] edge_temp=new int[edgeSize];
for(int i=0;i<edgeSize;i++){
int p1=cur_edge[i].start;
int p2=cur_edge[i].end;
int m=getEnd(edge_temp,p1);
int n=getEnd(edge_temp,p2);
//如果m和n没有连接在一起,他们就不相等
//如果相等就有回路
if(m!=n){
rets[index++]=cur_edge[i];
if(m>n){
int temp=n;
n=m;m=temp;
}
edge_temp[m]=n;
}
}
int lengh = 0;
for(int i = 0; i < index; i++) {
lengh+= rets[i].weight;
}
System.out.println("最小生成树的权重之和" + lengh);
char[] chars = new char[verticeSize];
chars[0] = 'A';
chars[1] = 'B';
chars[2] = 'C';
chars[3] = 'D';
chars[4] = 'E';
chars[5] = 'F';
chars[6] = 'G';
for (int i = 0; i < index; i++) {
System.out.printf("(%s, %s)---> %d \n",chars[rets[i].start], chars[rets[i].end], matrix[rets[i].start][rets[i].end]);
}
}
private int getEnd(int[] edge_temp, int p) {
int i=p;
while(edge_temp[i]!=0){
i=edge_temp[i];
}
return i;
}
}