贪心算法 - 最小生成树 Kruskal算法

关于最小生成树的概念,请参考前一篇文章:Prim算法。
Kruskal算法:
不停地循环,每一次都寻找两个顶点,这两个顶点不在同一个真子集里,且边上的权值最小。
把找到的这两个顶点联合起来。
初始时,每个顶点各自属于自己的子集合,共n个子集合。
每一步操作,都会将两个子集合融合成一个,进而减少一个子集合。
结束时,所有的顶点都在同一个子集合里,这个子集合就是最小生成树。
例子:

这里写图片描述

算法过程为:

这里写图片描述

代码实现:

public class Kruskal {  

    public static final int NOT_REACHED = -1;  

    public static void kruskal(int[][] E) {  
        int n = E.length;  
        MinHeap<Edge> edges = new MinHeap<Edge>(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<Edge> {  

    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 + "";  
    }  
}  
/**
 *             0
 *            / \
 *           1   2
 *          / \
 *         3  4
 *
 */
public class MinHeap<T extends Comparable> {
    private Object[] data;

    private int size;
    public int size()
    {
        return 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;
    }
}

运行结果:

1~3 : 1
4~6 : 2
2~5 : 3
3~6 : 4
2~3 : 5

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值