关于最小生成树的概念,请参考前一篇文章:Prim算法。
Kruskal算法:
不停地循环,每一次都寻找两个顶点,这两个顶点不在同一个真子集里,且边上的权值最小。
把找到的这两个顶点联合起来。
初始时,每个顶点各自属于自己的子集合,共n个子集合。
每一步操作,都会将两个子集合融合成一个,进而减少一个子集合。
结束时,所有的顶点都在同一个子集合里,这个子集合就是最小生成树。
例子:
算法过程为:
代码实现:
public class Kruskal {
public static final int NOT_REACHED = -1;
public static void kruskal(int[][] E) {
int n = E.length;
MinHeap edges = new MinHeap(n * (n - 1) / 2);
int[] setNum = new int[n];
for (int i = 0; i < n; i++) {
setNum[i] = i; // 初始时,每个顶点是一个子集合
}
// 初始化最小堆
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (isReachable(E, i, j)) {
edges.add(new Edge(i, j, E[i][j]));
}
}
}
Edge edge;
int count = n, v1, v2, num;
while ((count > 1) && (edge = edges.removeMin()) != null) {
v1 = edge.v1;
v2 = edge.v2;
if (setNum[v1] == setNum[v2]) continue; // 两个顶点在同一个子集合里
count--; // 融合两个子集合,减少了一个子集合
num = setNum[v2];
for (int i = 0; i < n; i++) {
if (setNum[i] == num) {
setNum[i] = setNum[v1]; // 把两个子集合融合到一个
}
}
System.out.println((v1+1) + "~" + (v2+1) + " : " + edge.len);
}
}
private static boolean isReachable(int[][] E, int v1, int v2) {
return E[v1][v2] != NOT_REACHED;
}
public static void main(String[] args) {
int[][] E = { { -1, 6, 1, 5, -1, -1 }, { 6, -1, 5, -1, 3, -1 },
{ 1, 5, -1, 5, 6, 4 }, { 5, -1, 5, -1, -1, 2 },
{ -1, 3, 6, -1, -1, 6 }, { -1, -1, 4, 2, 6, -1 } };
kruskal(E);
}
}
class Edge implements Comparable {
public int v1, v2, len;
public Edge(int v1, int v2, int len) {
this.v1 = v1;
this.v2 = v2;
this.len = len;
}
@Override
public int compareTo(Edge o) {
return len - o.len;
}
@Override
public String toString() {
return len + "";
}
}
1~3 : 1
4~6 : 2
2~5 : 3
3~6 : 4
2~3 : 5
最小堆的代码实现:
/**
* 0
* / \
* 1 2
* / \
* 3 4
* @author xuefeng
*
*/
public class MinHeap {
private Object[] data;
private int size;
public MinHeap(int capacity) {
data = new Object[capacity];
size = 0;
}
public boolean add(T val) {
if (size >= data.length)
return false;
int i = size, p;
data[size] = val;
size++;
while (i > 0) {
p = parentIndex(i);
if (get(i).compareTo(get(p)) < 0) {
swap(data, i, p);
} else {
break;
}
i = p;
}
return true;
}
public T remove(int index) {
if (index >= size)
return null;
int i = index, left, right, p;
T val = (T) data[index];
data[index] = data[size - 1];
size--;
while (!isLeaf(i)) {
left = leftIndex(i);
right = rightIndex(i);
p = i;
i = right >= size || get(left).compareTo(get(right)) < 0 ? left
: right;
if (get(i).compareTo(get(p)) < 0) {
swap(data, i, p);
}
}
return val;
}
public T removeMin() {
return remove(0);
}
public T get(int index) {
if (index >= size)
throw new IllegalArgumentException("index is greater than size : "
+ index);
return (T) data[index];
}
private static int leftIndex(int index) {
return 2 * index + 1;
}
private static int rightIndex(int index) {
return 2 * index + 2;
}
private static int parentIndex(int i) {
return i % 2 == 0 ? i / 2 - 1 : i / 2;
}
private boolean isLeaf(int index) {
return leftIndex(index) >= size;
}
private void swap(Object[] data, int i1, int i2) {
Object temp = data[i1];
data[i1] = data[i2];
data[i2] = temp;
}
}