最小生成树----Prim算法,. Kruskal 算法

目录

Prim

Kruskal


Prim

prim和disjkra类似的思想!简单

推荐这个版本:优先队列的JAva版本力扣

class Solution {
    public int minCostConnectPoints(int[][] points) {
        // 转化成无向图邻接表的形式
        List<int[]>[] graph = buildGraph(points);
        // 执行 Prim 算法
        Prim prim = new Prim(graph);
        return prim.minWeightSum;
    }

    // 转化成无向图邻接表的形式
    public List<int[]>[] buildGraph(int[][] points){
        // 图中共有 n 个节点
        int n = points.length;
        List<int[]>[] graph = new List[n];
        for(int i = 0; i < n; i++){
            graph[i] = new ArrayList<>();
        }
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                if(i == j){
                    continue;
                }
                int x1 = points[i][0], y1 = points[i][1];
                int x2 = points[j][0], y2 = points[j][1];
                int weight = Math.abs(x1 - x2) + Math.abs(y1 - y2);
                // 用 points 中的索引表示坐标点
                // 无向图其实就是双向图
                // 一条边表示为 int[]{from, to, weight}
                graph[i].add(new int[]{i, j, weight});
                graph[j].add(new int[]{j, i, weight});
            }
        }
        return graph;
    }

    class Prim{
        // 核心数据结构,存储横切边的优先级队列
        // 按照边的权重从小到大排序
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> (a[2] - b[2]));
        // 类似 visited 数组的作用,记录哪些节点已经成为最小生成树的一部分
        boolean[] inMST;
        // 记录最小生成树的权重和
        int minWeightSum = 0;
        // graph 是用邻接表表示的一幅图,
        // graph[s] 记录节点 s 所有相邻的边,
        // 三元组 int[]{from, to, weight} 表示一条边
        List<int[]>[] graph;

        public Prim(List<int[]>[] graph){
            this.graph = graph;
            inMST = new boolean[graph.length];
            // 随便从一个点开始切分都可以,从节点 0 开始
            cut(0);
            inMST[0] = true;
            // 不断进行切分,向最小生成树中添加边
            while(!pq.isEmpty()){
                int[] edge = pq.poll();
                int to = edge[1];
                int weight = edge[2];
                if(inMST[to]){
                    // 节点 to 已经在最小生成树中,跳过
                    // 否则这条边会产生环
                    continue;
                }
                // 将边 edge 加入最小生成树
                // 节点 to 加入后,进行新一轮切分,会产生更多横切边
                cut(to);
                inMST[to] = true;
                minWeightSum += weight;
            }
        }

        // 将 s 的横切边加入优先队列
        public void cut(int v){
            List<int[]> edges = graph[v];
            // 遍历 s 的邻边
            for(int[] edge : edges){
                if(inMST[edge[1]]){
                    // 相邻接点 to 已经在最小生成树中,跳过
                    // 否则这条边会产生环
                    continue;
                }
                // 加入横切边队列
                pq.add(edge);
            }
        }
    }
}

作者:雷莫
链接:https://leetcode.cn/problems/min-cost-to-connect-all-points/solutions/2145799/1584-lian-jie-suo-you-dian-de-zui-xiao-f-i12e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

未优化的C++版本

void Prim( Graph G)
{
	int lowcost[MAXSIZEF];
	int closest[MAXSIZE];
	int min;
	int sum=0;
	n  = G.vernum;
	for(int i=2;i<=n;i++)
	{
		lowcost[i]=G.edge[1][i];
		closest[i] =1;
	}
	lowcost[1] = INF;
	for(int i=2;i<=n;i++)
	{//循环n-1次
		min = INF;
		for(int j=2;j<=n;j++)
		{
			
			if(lowcost[j]<min)
			{
				k=j;
				min = lowcost[j]
			}
		}//找到最小lowcost
		cout<<closet[k]<<k;
		sum+=min;
		LOWCOST[k] =INF;
		for(int j =2;j<=n;j++)
		{
			if(G.[k][j]<lowcost[j]&&lowcost[j]<INF)
			{
				lowcost[j] = G.edge[k][j];
				closest[j] = k;
			}
		}
	}
	
}

参考::::::力扣

java代码  prim-

class Solution {
    public int minCostConnectPoints(int[][] points) {
        int N = points.length;
        int[][] newPoints = new int[N][N];
        int res = 0;
    //建立邻接矩阵
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            newPoints[i][j] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
            newPoints[j][i] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
        }
    }
    //lowcast存放与连接图中顶点的最小值
    int lowcast[] = new int[N];
    List<Integer> count = new ArrayList<>();
    count.add(0);
    lowcast[0] = -1;
    for (int i = 1; i < N; i++) {
        lowcast[i] = newPoints[i][0];
    }
    while (count.size() < N) {
        //选取lowcast数组中与已在图中的顶点最小距离的点
        int minDistance = Integer.MAX_VALUE;
        int flag = 0;
        for (int i = 0; i < N; i++) {
            if (lowcast[i] > 0 && lowcast[i] < minDistance) {
                flag = i;
                minDistance = lowcast[i];
            }
        }
            //将选取的点加入图中
            count.add(flag);
            //计入最短距离
            res += minDistance;
            //更新lowcast
            for (int j = 0; j < N; j++) {
                if (j == flag) {
                    lowcast[j] = -1;
                } else {
                    if (lowcast[j] != -1)
                        lowcast[j] = Math.min(lowcast[j], newPoints[j][flag]);
                }
            }
        }
    return res;
}
}

Kruskal

之前那个prim是不断把目前有最短的那个点加入到集合

这个Kruska,直接把所有边按权重降序排列,依次

直接把最短边加进来,途中判断是不是连通就行

推荐版本2:自己做的力扣

class Solution {
    class Edge{
        int from;
        int to;
        int weight;
        public Edge(int from,int to,int weight)
        {
            this.from=from;
            this.to=to;
            this.weight=weight;
        }
    }
    public int minCostConnectPoints(int[][] points) {
        List<Edge> graph = new ArrayList<>();
        int n = points.length;
        for(int i=0;i<n;i++)
        {
            int xi = points[i][0];
            int yi = points[i][1];
            for(int j=i+1;j<n;j++)
            {
                int xj =points[j][0];
                int yj = points[j][1];
                int dis = Math.abs(xi-xj) + Math.abs(yi-yj);
                Edge edge1 = new Edge(i,j,dis);
                Edge edge2 = new Edge(j,i,dis);
                graph.add(edge1);
                graph.add(edge2);
            }
        }
        
        Collections.sort(graph, (a,b)->a.weight-b.weight);
        UF uf = new UF(n);
        int minWeight= 0;
        for(Edge edge:graph){
            if(uf.isConnected(edge.from,edge.to)){
                continue;
            }
            uf.union(edge.from,edge.to);
            minWeight += edge.weight;
        }
        return minWeight;
    }
    class UF{
        int[] parent;
        int count;
        public UF(int count)
        {
            this.parent = new int[count];
            for(int i=0;i<count;i++){
                parent[i]=i;
            }
            this.count=count;
        }

        public void union(int x,int y){
            int rootX = findRoot(x);
            int rootY = findRoot(y);
            if(rootX==rootY){
                return;
            }
            parent[rootX]=rootY;
            count--;
        }
        public int findRoot(int x)
        {
            if(parent[x]!=x)
            {
                parent[x] = findRoot(parent[x]);
            }
            return parent[x];
        }
        public boolean isConnected(int x, int y)
        {
            int rootX = findRoot(x);
            int rootY = findRoot(y);
            if(rootX==rootY)
                return true;
            return false;
        }
        public int getCount()
        {
            return this.count;
        }
    }
}

推荐版本:别人写的力扣

class Solution {
    public int minCostConnectPoints(int[][] points) {
        int n = points.length;
        // 生成所有边及权重
        ArrayList<int[]> edges = new ArrayList<>();
        for(int i = 0; i < n; i++){
            // ⭐注意j从i+1开始,不要重头开始,避免重复
            for(int j = i + 1; j < n; j++){
                int x1 = points[i][0], y1 = points[i][1];
                int x2 = points[j][0], y2 = points[j][1];
                int dis = Math.abs(x1 - x2) + Math.abs(y1 - y2);
                // 用坐标点在 points 中的索引表示坐标点
                edges.add(new int[]{i, j, dis});
            }
        }

        // 将边按照权重从小到大排序
        Collections.sort(edges, (a, b) -> (a[2] - b[2]));

        // 执行 Kruskal 算法
        UF uf = new UF(n);
        int minWeight = 0;
        for(int[] edge : edges){
            int i = edge[0];
            int j = edge[1];
            // 若这条边会产生环,则不能加入 mst
            if(uf.isConnected(i, j)){
                continue;
            }
            // 若这条边不会产生环,则属于最小生成树
            uf.union(i, j);
            minWeight += edge[2];
        }

        return minWeight;
    }

    class UF{
        private int count;
        private int[] parent;

        public UF(int n){
            parent = new int[n];
            count = 0;
            for(int i = 0; i < n; i++){
                parent[i] = i;
            }
        }

        public void union(int p, int q){
            int rootP = findRoot(p);
            int rootQ = findRoot(q);
            parent[rootP] = rootQ;
            // 两个连通分量合并成一个连通分量,count数量减一
            count--;
        }

        public boolean isConnected(int p, int q){
            int rootP = findRoot(p);
            int rootQ = findRoot(q);
            return rootP == rootQ;
        }

        public int findRoot(int x){
            if(parent[x] != x){
                parent[x] = findRoot(parent[x]);
            }
            return parent[x];
        }
        
        public int getCount(){
            return count;
        }
    }
}

作者:雷莫
链接:https://leetcode.cn/problems/min-cost-to-connect-all-points/solutions/2145799/1584-lian-jie-suo-you-dian-de-zui-xiao-f-i12e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fars

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值