连接所有点的最小费用_力扣1584_Prim算法_Kruskal算法

该问题要求找到将所有二维平面上的点通过曼哈顿距离连接起来的最小总费用。可以通过Prim算法或Kruskal算法求解最小生成树来解决。文章提供了两种算法的实现,一种是基于Prim的邻接矩阵方法,另一种是基于Kruskal的并查集方法。
摘要由CSDN通过智能技术生成
连接所有点的最小费用

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/min-cost-to-connect-all-points

给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。

连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的曼哈顿距离:|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。

请你返回将所有点连接的最小总费用。只有任意两点之间有且仅有一条简单路径时,才认为所有点都已连接。

示例 1:

在这里插入图片描述

输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
输出:20
解释:
在这里插入图片描述
我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。
注意到任意两个点之间只有唯一一条路径互相到达。

示例 2:

输入:points = [[3,12],[-2,5],[-4,1]]
输出:18

示例 3:

输入:points = [[0,0],[1,1],[1,0],[-1,1]]
输出:4

示例 4:

输入:points = [[-1000000,-1000000],[1000000,1000000]]
输出:4000000

示例 5:

输入:points = [[0,0]]
输出:0

提示:
1 <= points.length <= 1000
-106 <= xi, yi <= 106
所有点 (xi, yi) 两两不同。

最小生成树

给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成树。如果边上有权值,那么使得边权和最小的生成树叫做最小生成树。

Prim算法

首先,假设有一棵只包含一个顶点v的树T。然后贪心地选取T和其他顶点之间相连的最小权值的边,并把它加到T中。不断进行这个操作,就可以得到一棵生成树了。通过这个方法得到的生成树就是最小生成树。时间复杂度:O(n2)

抽象出两个集合,集合V和集合Vnew
1、集合V保存未加入到最小生成树中的节点,最开始,图中所有节点都在集合V中。
2、集合Vnew保存已经加入到最小生成树中的节点,如果一个节点加入到了最小生成树中,则将该节点加入到集合Vnew。
说明:Vnew即最小生成树

func minCostConnectPoints(points [][]int) int {
    n := len(points) //顶点数
    //cost[u][v]表示边e=(u,v)的权值(不存在的情况下设为INF)
    cost := make([][]int,n) 
    for i:=0;i<n;i++{
        cost[i] = make([]int,n)
    }
    for i:=0;i<n;i++{
        for j:=i+1;j<n;j++{
            dist := abs(points[i][0]-points[j][0])+abs(points[i][1]-points[j][1])
            cost[i][j],cost[j][i] = dist,dist
        }
    }
    mincost := make([]int,n) //从集合Vnew出发的边到每个顶点的最小权值
    used := make([]bool,n) //顶点i是否包含在集合Vnew中
    var prim func() int
    prim = func() int{
        for i:=0;i<n;i++{
            mincost[i] = math.MaxInt32
            used[i] = false
        }
        mincost[0] = 0
        res := 0
        for true{
            v := -1
            //从不属于Vnew的顶点中选取从Vnew到其权值最小的顶点
            for u:=0;u<n;u++{
                if !used[u] && (v==-1 || mincost[u]<mincost[v]){
                    v=u
                }
            }
            if v == -1{
                break
            }
            used[v] = true //将顶点v加入Vnew中
            res += mincost[v] //将边的长度加入结果里
            for u:=0;u<n;u++{
                mincost[u] = min(mincost[u],cost[v][u])
            }
        }
        return res
    }
    return prim()
}

func min(a,b int) int{
    if a<b{
        return a
    }
    return b
}

func abs(a int) int{
    if a<0{
        return -a
    }
    return a
}
Kruskal算法

Kruskal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈(重边等也算在内),就把当前这条边加入到生成树中。
如何判断是否产生圈:假设现在要把连接顶点u和顶点v的边e加入生成树中。如果加入之前u和v不在同一个连通分量里,那么加人e也不会产生圈。反之,如果u和v在同一个连通分量里,那么一定会产生圈。可以使用并查集高效地判断是否属于同一个连通分量。
Kruskal算法在边的排序上最费时,当使用时间复杂度为O(eloge)的排序方法时,Kruskal算法的时间复杂度即为O(eloge),所以,适合于求边稀疏的网的最小生成树。

func minCostConnectPoints(points [][]int) int {
    n := len(points)
    type edge struct{
        u,v,dist int
    }
    edges := []edge{}
    for i:=0;i<n;i++{
        for j:=i+1;j<n;j++{
            edges = append(edges,edge{i,j,dis(points[i],points[j])})
        }
    }
    sort.Slice(edges, func(i, j int) bool { return edges[i].dist < edges[j].dist })
    uf := newUnionFind(n)
    left := n - 1
    res := 0
    for _, e := range edges {
        if uf.union(e.u, e.v) {
            res += e.dist
            left--
            if left == 0 {
                break
            }
        }
    }
    return res
}

func dis(p, q []int) int {
    return abs(p[0]-q[0]) + abs(p[1]-q[1])
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

type unionFind struct {
    parent, rank []int
}

func newUnionFind(n int) *unionFind {
    parent := make([]int, n)
    rank := make([]int, n)
    for i := range parent {
        parent[i] = i
        rank[i] = 1
    }
    return &unionFind{parent, rank}
}

func (uf *unionFind) find(x int) int {
    if uf.parent[x] != x {
        uf.parent[x] = uf.find(uf.parent[x])
    }
    return uf.parent[x]
}

func (uf *unionFind) union(x, y int) bool {
    fx, fy := uf.find(x), uf.find(y)
    if fx == fy {
        return false
    }
    if uf.rank[fx] < uf.rank[fy] {
        fx, fy = fy, fx
    }
    uf.rank[fx] += uf.rank[fy]
    uf.parent[fy] = fx
    return true
}

做个笔记,如有错误欢迎大家指正!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值