大致原理实现如下: 把所有的边放在一个集合里,每次都贪心的取出最短的边(权重最小),直到取出的边数为顶点数减一。当然取的时候有条件限制:每次取出来的边不能和之前取出来的边形成回路(这个可以使用并查集来实现)。
并查集(具体解析查看(Java版)并查集的实现_南柏良客的博客-优快云博客),判断V和W是否属于同一颗集合树,如果是加上必然构成回路。
最小堆的实现:只需要在最大堆(最大堆的实现)的基础上稍加改动,改动精髓点在于向下过滤
节点的步骤: 必须要把每一个自定义节点的每一个属性都向下过滤(正确代码查看图二),否则会出现错误(有什么好的解决办法还请大神们指点迷津)
正确向下过滤节点法 (每一个属性都向下过滤):
错误过滤法:
具体代码实现如下 :
最小边堆
public class EdgeData {
public int weight;
public int Node1;
public int Node2;
}
public class MinEdgeHeap {
public static final int MinData = -99999;
EdgeData[] data;
int size;
int capacity;
private MinEdgeHeap create(int MaxSize) {
MinEdgeHeap minEdgeHeap = new MinEdgeHeap();
minEdgeHeap.data = new EdgeData[MaxSize*MaxSize];
minEdgeHeap.size = 0;
minEdgeHeap.capacity = MaxSize;
minEdgeHeap.data[0] = new EdgeData();
minEdgeHeap.data[0].weight = MinData; //建立哨兵,方便操作
return minEdgeHeap;
}
public boolean IsFull(MinEdgeHeap H) {
if(H.size == H.capacity) {
return true;
}
return false;
}
public boolean IsEmpty(MinEdgeHeap H) {
if(H.size == 0) {
return true;
}
return false;
}
public void insert(MinEdgeHeap H, int weight,int Node1,int Node2) {
if(IsFull(H)) {
System.out.println("最大堆已满");
return;
}
// 因为邻接表形成的边是有方向的,下面的for循环去方向
for(int i=1;i<=H.size;i++) {
if((H.data[i].Node1==Node1||H.data[i].Node1==Node2)&&(H.data[i].Node2==Node1||H.data[i].Node2==Node2)) {
return;
}
}
int i = ++H.size; //i指向插入元素后堆中最后一个元素的位置
H.data[i] = new EdgeData();
//这里利用堆(完全二叉树)的性质,每个子节点i/2必定得到父节点。称为向下过滤节点
for( ; H.data[i/2].weight>weight; i/=2) {
H.data[i].weight = H.data[i/2].weight;
H.data[i].Node1 = H.data[i/2].Node1;
H.data[i].Node2 = H.data[i/2].Node2;
}
H.data[i].weight = weight;
H.data[i].Node1 = Node1;
H.data[i].Node2 = Node2;
}
public EdgeData deleteMax(MinEdgeHeap H) {
int parent,child;
EdgeData MaxItem,temp;
if(IsEmpty(H)) {
System.out.println("最大堆已空");
return null;
}
MaxItem = H.data[1];
temp = H.data[H.size--];
//parent*2<H.size说明有儿子(左右至少一个)
for(parent = 1; parent*2<H.size; parent = child) {
child = parent*2; //假设左儿子为最大值
//child != H.size说明有右儿子,H.data[child]<H.data[child+1]右儿子更大
if((child != H.size)&&(H.data[child].weight>H.data[child+1].weight)) {
child++;
}
if(temp.weight<=H.data[child].weight) {
break;
}
else {
H.data[parent] = H.data[child];
}
}
H.data[parent] = temp;
return MaxItem;
}
public MinEdgeHeap createEdgeHeap(Vertex[] vertexList) {
MinEdgeHeap minEdgeHeap = new MinEdgeHeap();
MinEdgeHeap heap = minEdgeHeap.create(vertexList.length*vertexList.length);
for(int i=0;i<vertexList.length;i++) {
EdgeNode p = vertexList[i].firstEdge;
while(p != null) {
minEdgeHeap.insert(heap, p.weight, i, p.adjvex);
p = p.next;
}
}
return heap;
}
}
并查集
//并查集
public class Assemble {
public char data;
public int parent;
public int find(Assemble[] assemble,char data) {
int i ;
for(i = 0; i<assemble.length && assemble[i].data != data;i++);
if(i >= assemble.length) return -1;
for(; assemble[i].parent >=0 ;i = assemble[i].parent);
return i;
}
public void union(Assemble[] assemble,char data1,char data2) {
int Root1,Root2;
Root1 = find(assemble, data1);
Root2 = find(assemble, data2);
if(Root1 != Root2) {
if(assemble[Root1].parent <= assemble[Root2].parent) {
assemble[Root1].parent = assemble[Root1].parent+assemble[Root2].parent;
assemble[Root2].parent = Root1;
}
if(assemble[Root1].parent > assemble[Root2].parent) {
assemble[Root2].parent = assemble[Root1].parent+assemble[Root2].parent;
assemble[Root1].parent = Root2;
}
}
}
}
Kruskal算法如下:
import maxHeap.Assemble;
import maxHeap.EdgeData;
import maxHeap.MinEdgeHeap;
import maxHeap.Vertex;
public class Kruskal {
public void kruskal(Vertex[] vertexList) {
EdgeData[] NodeList = new EdgeData[vertexList.length-1];
Assemble[] asmb = new Assemble[vertexList.length];
for(int i=0;i<vertexList.length;i++) {
asmb[i] = new Assemble();
asmb[i].data = vertexList[i].data;
asmb[i].parent = -1;
}
MinEdgeHeap minEdgeHeap = new MinEdgeHeap();
MinEdgeHeap heap = minEdgeHeap.createEdgeHeap(vertexList);
int j=0;
while((NodeList[vertexList.length-2] == null)&&(!minEdgeHeap.IsEmpty(heap))) {
EdgeData data = minEdgeHeap.deleteMax(heap);
Assemble assemble = new Assemble();
if(assemble.find(asmb, vertexList[data.Node1].data)!=
assemble.find(asmb, vertexList[data.Node2].data)) {
NodeList[j++] = data;
assemble.union(asmb, vertexList[data.Node1].data, vertexList[data.Node2].data);
}
}
if(NodeList[vertexList.length-2] == null) {
System.out.println("生成树不存在或图不连通");
}
for(int i = 0;i<NodeList.length;i++) {
System.out.println(vertexList[NodeList[i].Node1].data+" "+vertexList[NodeList[i].Node2].data);
}
}
}
简单写个测试类:
import map.Kruskal;
public class Test2 {
public static void main(String[] args) {
char[] vexs = {'A','B','C','D','E','F','G'};
char[][] edges = new char[][] {
{'A','B',7},
{'A','D',6},
{'A','C',9},
{'B','C',13},
{'B','G',3},
{'D','C',2},
{'D','E',6},
{'E','C',10},
{'E','F',7},
{'F','C',6},
{'F','G',6},
{'G','C',6},
};
CreateVertexList createVertexList = new CreateVertexList();
Vertex[] vertexList = createVertexList.createListNDG(vexs, edges);
Kruskal kruskal = new Kruskal();
kruskal.kruskal(vertexList);
}
}
传入的图:
测试类输出结果如下:
形象化,如下便是形成的最小生成树: