【数据结构】【Java】最小生成树之Kruskal

基本思想:每次从剩下的边中选择一条权重最小并且不会产生环路的边加入到已选择的边集当中。(连树成森林)
Kruskal
图源:yizhe-shi(LeetCode)
链接: 顺便可以看看这位大佬的题解.

难点1 : 如何找到权重最小边? —— 使用最小堆
难点2 : 如何判断不产生回路?—— 每个顶点都是一个并查集,先从最小堆中选出一条权重最小的边,如果该边的两个顶点位于两个并查集中,即说明不会产生回路。每次添加成功一条边后,需要把两个顶点所在的并查集合并。

需要使用的数据结构:用于存储图的边的数组、用于提取边的小根堆、用于存储最小生成树的边的数组、用于表示子图的并查集

由于Java没有自带的并查集,咱们先自己实现一个:
链接: 这个大佬讲得并查集真的很有意思.

实现并查集

public class UnionFind
{
	private int[] id;
	private int count;

	public UnionFind(int n)
	{
		count = n;
		id = new int[n];
		for(int i = 0; i < n; i++) id[i] = i;
	}
	public int getCount()
	{
		return count;
	}
	public boolean connected(int p, int q)
	{
		return find[p] == find[q];
	}
	public int find(int p)
	{
		return id[p];
	}
	public void union(int p, int q)
	{
		int pRoot = find(p);
		int qRoot = find(q);
		if(pRoot == qRoot) return;
		for(int i = 0; i < id.length; i++)
		{
			if(id[i] == pRoot) id[i] = qRoot;
		}
		count--;
	}
}

子图的表示:使用并查集。
判断边(u,v)是否产生环:判断u,v是否处在同一个并查集。
加入一条边:两个并查集合并。

开始表演:

public ArrayList<weightedEdge> kruskal()
{
	int n = numberOfVertices();
	int e = numberOfEdges();
	ArrayList<weightedEdge> res = new ArrayList<>();//用于存储结果

	//将边存入一个数组中
	int k = 0;
	weightedEdge[] edges = new weightedEdge[e];
	for(int i = 0; i < n; i++)
	{
		for(int j = i + 1; j <= n; j++)
		{
			if(edgeExist(i, j))
				edges[k++] = new weightedEdge(edge(i, j));
		}
	}

	//使用小根堆存储数组中的边
	PriorityQueue<weightedEdge> minHeap = new PriorityQueue<>(e, new Comparator<weightedEdge>()
	{
		public int compare(weightedEdge i, weightedEdge j)
		{
			return i.weight - j.weight;
		}
	});
	for(weightEdge e : edges)
	{
		minHeap.offer(e);
	}
	
	//使用并查集合并子图
	k = 0;//计数
	UnionFind uf = new UnionFind(n);//n个顶点n个子图
	while(e > 0 && k < n - 1)
	{
		weightedEdge we = minHeap.poll();
		e--;
		int a = we.vertex1();
		int b = we.vertex2();
		if(!uf.connected(a, b))//选出的该边的两个顶点在不同并查集中
		{
			res.add(we);
			k++;
			uf.unite(a, b);
		}
	}
	return res; 
}

这里再推荐一道练手题:
链接: LeetCode 1584.连接所有点的最小费用.
以下代码是我完全按照上述格式写出来的题解:

class Solution {
    public int minCostConnectPoints(int[][] points) {
        int len = points.length;
        if(len == 1) return 0;
        WeightedEdge[] edges = new WeightedEdge[len*(len-1)/2];
        int k = 0;
        int sum = 0;
        ArrayList<WeightedEdge> res = new ArrayList<>(len);

        for(int i = 0; i < len; i++)
        {
            for(int j = i + 1; j < len; j++)
            {
                int w = Math.abs(points[i][0]-points[j][0]) + Math.abs(points[i][1]-points[j][1]);
                edges[k++] = new WeightedEdge(i, j, w);
            }
        }

        PriorityQueue<WeightedEdge> minHeap = new PriorityQueue<>(len*(len-1)/2, new Comparator<WeightedEdge>()
        {
            public int compare(WeightedEdge i, WeightedEdge j){
                return i.weight - j.weight;
            }
        });
        for(WeightedEdge edge : edges) minHeap.offer(edge);

        UnionFind uf = new UnionFind(len);
        k = 0;
        while(k < len-1)
        {
            WeightedEdge we = minHeap.poll();
            int a = uf.find(we.vertex1);
            int b = uf.find(we.vertex2);
            if(a != b)
            {
                res.add(we);
                k++;
                uf.union(we.vertex1, we.vertex2);
            }
        }

        for(WeightedEdge every : res) sum += every.weight;
        return sum;
    }
}


class WeightedEdge{
    public int vertex1, vertex2, weight;
    WeightedEdge(int i, int j, int w)
    {
        vertex1 = i;
        vertex2 = j;
        weight = w;
    }
}


class UnionFind{
    private int[] id;
    private int count;
    UnionFind(int n)
    {
        id = new int[n];
        for(int i = 0; i < n; i++) id[i] = i;
        count = n;
    }
    public int find(int p)
    {
        return id[p];
    }
    public void union(int p, int q)
    {
        int a = find(p);
        int b = find(q);
        if(a != b)
        {
            for(int i = 0; i < id.length; i++)
            {
                if(id[i] == a) id[i] = b;
            }
            count--;
        }
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值