每日一题 连接所有点的最小费用 LeetCode1584

69 篇文章 0 订阅
44 篇文章 0 订阅

由完全图构建最小生成树,主要有两种方法:1. Kruskal算法;2. Prim算法;

Kruskal算法

需要构建并查集检查每次新添加的边构不构成回路。

// 本质是求解一个完全图的最小生成树;
// 使用kruskal算法需要通过并查集判断连通性,使用Prim算法则不用;
// 首先需要将所有边都提取到一个边集里面,然后对边长进行排序;
// 每次选取最短的边,然后通过并查集判断连通性

// lion is hungry

class DisjointSet {
private:
    vector<int> f, rank; // 按size大小进行优化

public:
    DisjointSet(int n): f(n), rank(n) {
        for (int i = 0; i < n; ++i) {
            f[i] = i;
            rank[i] = 1;
        }
    }

    int findf(int x) {
        if (f[x] != x) {
            f[x] = findf(f[x]);
        }
        return f[x];
    }

    bool unionSet(int x, int y) { // 判断是否处于同一个连通分量并合并
        int fx = findf(x), fy = findf(y);
        if (fx == fy) return false;
        if (rank[fx] < rank[fy]) swap(fx, fy);
        rank[fx] += rank[fy];
        f[fy] = fx;
        return true;
    }
};

struct Edge() { // 使用一个struct表示一条边,要存储边的长度以及两个顶点
    int len, x, y;
    Edge(int len, int x, int y) : len(len), x(x), y(y) {};
}


class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        auto dist = [&] (int x, int y) -> int { // 声明一个匿名函数,用来计算两点之间的曼哈顿距离
            return abs(points[x][0] - points[y][0]) + abs(points[x][1] - points[y][1]);
        }
        int n = points.size();
        DisjointSet dsu(n);
        vector<Edge> edges;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                edges.emplace_back({dist(i, j), i, j});
            }
        }
        sort(edges.begin(), edges.end(), [](Edge a, Edge b) -> int {return a.len < b.len}); // 按升序的顺序排列所有边
        int ret = 0, num = 1;
        for (auto& [len, x, y] : edges) {
            if (dsu.unionSet(x, y)) {
                ret += len;
                num++;
                if (num == n) {
                    break;
                }
            }
        }
        return ret;
    }
};

Prim算法

每次寻找距离当前已生成的连通分量距离最近的顶点加入。

class Solution {
public:
    int prim(vector<vector<int> >& points, int start) {
        int n = points.size();
        int res = 0;
        // 将points转换成临接矩阵
        vector<vector<int>> g(n, vector<int>(n));
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int dist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);
                g[i][j] = dist;
                g[j][i] = dist;
            }
        }

        vector<int> lowcost(n, INT_MAX); // 记录Vi到已生成连通分量的最短距离
        vector<int> v(n, -1); // 记录Vi是否已经加入了所生成的连通分量

        // 先将start结点加入生成树,然后更新距离
        v[start] = 0;
        for (int i = 0; i < n; ++i) {
            if (i == start) continue;
            lowcost[i] = g[i][start];
        }

        // 加入剩下n - 1个结点
        for (int i = 1; i < n; ++i) {
            // 要执行n - 1次以添加n - 1个结点
            int minIdx = -1;
            int minVal = INT_MAX;
            // 选出距离当前连通分量距离最近的顶点
            for (int j = 0; j < n; ++j) {
                if (v[j] == 0) continue;
                if (lowcost[j] < minVal) {
                    minIdx = j;
                    minVal = lowcost[j];
                }
            }
            
            // 加入这个顶点并更新距离
            res += minVal;
            v[minIdx] = 0;
            lowcost[minIdx] = -1;
            for (int j = 0; j < n; ++j) {
                if (v[j] == -1 && lowcost[j] > g[minIdx][j]) {
                    lowcost[j] = g[minIdx][j];
                }
            }
        }
        return res;
    }

    int minCostConnectPoints(vector<vector<int>>& points) {
        return prim(points, 0);
    }
};

采用堆优化的Prim算法。
虽然感觉这个堆优化然并卵。。。

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 优先队列, pair的第一个元素表示距离,第二个元素表示下标
        vector<bool> v(n, false); // 用来记录一个结点是否已经加入了连通分量;
        int result = 0;

        pq.push({0, 0});
        while (!pq.empty()) {
            auto [dist, i] = pq.top();
            pq.pop();
            if (v[i]) continue; // 这个点已经加入

            result += dist;
            v[i] = true;
            
            for (int j = 0; j < n; ++j) {
                if (v[j]) continue;
                int newDist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);
                pq.push({newDist, j});
            }
        }
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值