最小生成树MST
- **算法第四版- 4.3**
- 0.序
- 1.Prim算法
- 2. Kruskal算法
Prim算法,以顶点为单元,与图中边数无关,比较适合于稠密图
Kruskal算法,以边为顶点,时间主要取决于边数,适合稀疏图
下面代码的来源
采用的是曼哈顿距离。
class Solution { public: int prim(vector>& points, int start) { int n = points.size(); int res = 0; // 1. 将points转化成邻接矩阵, 这一步可有可无 vector > g(n, vector (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; } } // 记录V[i]到Vnew的最近距离 vector lowcost(n, INT_MAX); // 记录V[i]是否加入到了Vnew vector v(n, -1); // 2. 先将start加入到Vnew v[start] = 0; for (int i = 0; i < n; i++) { if (i == start) continue; lowcost[i] = g[i][start]; } // 3. 剩余n - 1个节点未加入到Vnew,遍历 for (int i = 1; i < n; i++) { // 找出此时V中,离Vnew最近的点 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]; } } // 将该点加入Vnew,更新lowcost和v res += minVal; v[minIdx] = 0; lowcost[minIdx] = -1; // 更新集合V中所有点的lowcost for (int j = 0; j < n; j++) { if (v[j] == -1 && g[j][minIdx] < lowcost[j]) { lowcost[j] = g[j][minIdx]; } } } return res; } int minCostConnectPoints(vector >& points) { return prim(points,0); } };
楼上是O(n^2)
再补充一个堆优化的,O(mlogn),m为边的数目
class Solution { public: int prim(vector2. Kruskal算法>& points, int start) { int n = points.size(); if (n == 0) return 0; int res = 0; // 将points转化成邻接表 vector > g(n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) continue; g[i].push_back(j); g[j].push_back(i); } } // 记录V[i]到Vnew的最近距离 vector lowcost(n, INT_MAX); // 记录V[i]是否加入到了Vnew vector v(n, -1); // 格式:<距离, 下标> priority_queue , vector >, greater<> > pq; pq.push(make_pair(0, start)); while (!pq.empty()) { auto [dist, i] = pq.top(); pq.pop(); if (v[i] == 0) continue; v[i] = 0; res += dist; for (int k = 0; k < g[i].size(); k++) { int j = g[i][k]; int w = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]); if (v[j] == -1 && lowcost[j] > w) { lowcost[j] = w; pq.push(make_pair(w, j)); } } } return res; } int minCostConnectPoints(vector >& points) { return prim(points, 0); } };
依次选择最小的边,看是否形成环
时间复杂度O(m log(m) + m α(m) )
class Djset { public: vectorparent; // 记录节点的根 vector rank; // 记录根节点的深度(用于优化) vector size; // 记录每个连通分量的节点个数 vector len; // 记录每个连通分量里的所有边长度 int num; // 记录节点个数 Djset(int n): parent(n), rank(n), len(n, 0), size(n, 1), num(n) { for (int i = 0; i < n; i++) { parent[i] = i; } } int find(int x) { // 压缩方式:直接指向根节点 if (x != parent[x]) { parent[x] = find(parent[x]); } return parent[x]; } int merge(int x, int y, int length) { int rootx = find(x); int rooty = find(y); if (rootx != rooty) { if (rank[rootx] < rank[rooty]) { swap(rootx, rooty); } parent[rooty] = rootx; if (rank[rootx] == rank[rooty]) rank[rootx] += 1; // rooty的父节点设置为rootx,同时将rooty的节点数和边长度累加到rootx, size[rootx] += size[rooty]; len[rootx] += len[rooty] + length; // 如果某个连通分量的节点数 包含了所有节点,直接返回边长度 if (size[rootx] == num) return len[rootx]; } return -1; } }; struct Edge { int start; // 顶点1 int end; // 顶点2 int len; // 长度 }; class Solution { public: int minCostConnectPoints(vector >& points) { int res = 0; int n = points.size(); Djset ds(n); vector edges; // 建立点-边式数据结构 for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { Edge edge = {i, j, abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])}; edges.emplace_back(edge); } } // 按边长度排序 sort(edges.begin(), edges.end(), [](const auto& a, const auto& b) { return a.len < b.len; }); // 连通分量合并 for (auto& e : edges) { res = ds.merge(e.start, e.end, e.len); if (res != -1) return res; } return 0; } };
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)