004-1【图结构】最小生成树

题目

1584. 连接所有点的最小费用

给你一个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

解析

每个point是一个vertex,两个point之间的曼哈顿距离是cost,转化为图的最小生成树问题。

代码

prim最小生成树


class Solution {
    int prim(vector<vector<int>>& graph,int n,int src){
        vector<bool> vis(n+1,false);//是否已加入最小生成树
        vis[src]=true;
        vector<int> dis(n+1,INT_MAX);//当前最小树到第i个节点的距离
        for(int i=1;i<=n;i++){
            dis[i]=graph[src][i];
        }

        //计算最小生成树
        for(int i=1;i<=n;i++){
            //1.寻找尚未搜索的集合中,最小代价的节点
            int minCost=INT_MAX;
            int v=1;
            for(int i=1;i<=n;i++){
                if(!vis[i]&&dis[i]<minCost){
                    minCost=dis[i];
                    v=i;
                }
            }

            //2.
            if(dis[v]==INT_MAX)break;

            //3.访问v节点,加入最小生成树,并更新v的相邻节点
            vis[v]=true;
            for(int i=1;i<=n;i++){
                if(!vis[i]&&graph[v][i]<dis[i]){
                    dis[i]=graph[v][i];
                }
            }
        }

        int cost=0;//总代价
        for(int i=1;i<=n;i++){
            if(dis[i]==INT_MAX)return -1;//不存在最小生成树
            cost+=dis[i];
        }
        return cost;
    }
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n=points.size();

        vector<vector<int>> graph(n+1,vector<int>(n+1));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                graph[i][j]=graph[j][i]=abs(points[i-1][0]-points[j-1][0])+abs(points[i-1][1]-points[j-1][1]);
            }
        }

        int ans=prim(graph,n,1);//总距离
        return ans;
    }
};

kruskal最小生成树

从小到大加入边,是一个贪心算法。

其算法流程为:

  1. 将图 G={V,E} 中的所有边按照长度由小到大进行排序,等长的边可以按任意顺序。
  2. 初始化图 G’{V,∅},从前向后扫描排序后的边,如果扫描到的边 e 在 G’ 中连接了两个相异的连通块,则将它插入 G’ 中。
  3. 最后得到的图 G′ 就是图 G 的最小生成树。
class DisjointSetUnion{
private:
	vector<int> parent;
	vector<int> rank;
	int n;
public:
	DisjointSetUnion(int n_){
		n=n_;
		rank.resize(n,1);
		parent.resize(n);
		for(int i=0;i<n;i++){
			parent[i]=i;
			rank[i]=0;
		}
	}
	int Find(int x){
		if(parent[x]!=x)
			parent[x]=Find(parent[x]);
		return parent[x];
	}
	void Union(int x,int y){
		int xRoot=Find(x);
		int yRoot=Find(y);
		if(xRoot==yRoot)
			return;
		//rank高的作为父节点
		if(rank[xRoot]>=rank[yRoot])
			parent[yRoot]=xRoot;
		else{
			parent[xRoot]=yRoot;
		}
	}
};
struct Edge {
    int len, from, to;
    Edge(int len, int from, int to) : len(len), from(from), to(to) {
    }
};
class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
		int n = points.size();
		vector<Edge> edges;
		for (int i = 0; i < n; ++i) {
			for (int j = i + 1; j < n; ++j) {
				int x = points[i][0] - points[j][0];
				int y = points[i][1] - points[j][1];
				edges.push_back(Edge(abs(x) + abs(y), i, j));
			}
		}
		sort(edges.begin(), edges.end(),[](const Edge& a, const Edge& b) {
			return a.len < b.len;
		});
		DisjointSetUnion dsu(n);
		int ans = 0;
		for (auto& e : edges) {
			if (dsu.Find(e.from) != dsu.Find(e.to)) {
				ans += e.len;
				dsu.Union(e.from, e.to);
			}
		}
		return ans;
	}
};

复杂度

prim:

T(n)=O(n2)

kruskal

时间复杂度:O(n2log(n)),其中 n是节点数。一般Kruskal 是 O(mlogm) 的算法,m是边数,但本题中 m=n2 ,因此总时间复杂度为O(n2log(n))

空间复杂度:O(n2),其中 n是节点数。并查集使用 O(n)的空间,边集数组需要使用 O(n2 ) 的空间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值