Prim 最小生成树算法 :: labuladong的算法小抄 #1028
Replies: 16 comments 8 replies
-
// 图中有 n 个节点 这里的n不是节点数量,而是存在的边的数量。 |
Beta Was this translation helpful? Give feedback.
-
我错了,n就是节点数量。。。可我不知道如何删除评论 |
Beta Was this translation helpful? Give feedback.
-
1135 题「 最低成本联通所有城市」,Prim算法的python实现 import heapq
import collections
class Solution:
# Kruscal并查集方式判定
class UnionFind(object):
def __init__(self, n):
self.parent = list(range(n))
def find(self, x):
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, x1, x2):
self.parent[self.find(x1)] = self.find(x2)
def connected(self, x1, x2):
return self.find(x1) == self.find(x2)
def minCostConnectPoints(self, points: List[List[int]]) -> int:
# 连边权重换成了曼哈顿距离
def manhattan_distance(p1, p2):
return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1])
# 1.uf并查集解决方案
n = len(points)
# uf = Solution.UnionFind(n)
# # 遍历构建点之间距离数组
distances = []
for i in range(n):
for j in range(i, n):
distances.append(
(i, j, manhattan_distance(points[i], points[j])))
# distances = sorted(distances, key=lambda x: x[-1])
# count = 0
# for dist in distances:
# x, y, x_y_dist = dist
# if uf.connected(x, y):
# continue
# uf.union(x, y)
# count += x_y_dist
# return count
# 2.BFS方式
def build_graph(connections): # 构建图
graph = collections.defaultdict(list)
# 无向图就是双向图
for conn in connections:
u, v, weight = conn
graph[u].append((u, v, weight))
graph[v].append((v, u, weight))
return graph
def cut(s):
for edge in graph[s]:
_from, _to, weight = edge
if in_mst[_to]:
continue
heapq.heappush(pq, (weight, _from, _to))
graph = build_graph(distances)
pq = []
# 记录MST中的节点
in_mst = [False] * n
# 记录最终结果
weight_sum = 0
in_mst[0] = True
cut(0)
while pq:
weight, _from, _to = heapq.heappop(pq)
if in_mst[_to]:
continue
weight_sum += weight
in_mst[_to] = True
cut(_to)
return weight_sum |
Beta Was this translation helpful? Give feedback.
-
东哥 什么时候能有c++版的答案呢? |
Beta Was this translation helpful? Give feedback.
-
什么时候出个弗洛依德算法呢!! |
Beta Was this translation helpful? Give feedback.
-
@ZackHongru 算法用的标准库就那么几个,看多了就习惯了。另外可以在评论区看看有没有别人的 cpp 代码。 |
Beta Was this translation helpful? Give feedback.
-
“Prim 算法的时间复杂度也是可以优化的,但优化点在于优先级队列的实现上” 能优于O(ElogE)吗?网上好像都是从array优化成priorityqueue,好像没有更优化的了 |
Beta Was this translation helpful? Give feedback.
-
@JerryWuZiJie priorityqueue 也有不同的实现,有些实现可以直接修改队列中的元素,这样的优先级队列可以进一步降低时间复杂度。Java 标准库的优先级队列不可以。 |
Beta Was this translation helpful? Give feedback.
-
对Prim算法的个人理解个人认为,对于Prim算法的理解,用教科书上的“加点”可以更好接受。设已被连入最小生成树的集合位MST,则每一次选取连接到MST中cost最小的点。因此优先队列里永远放的都是点和该点连接到MST需要的最小cost(其实也还是边的权值),然后随着算法的执行,每个点连接到MST的cost也会不断被更新(不断往优先级队列里插入更小的cost,原来的cost不会被删除)。这样想可以把Prim和Dijkstra完全类比,两个算法只有一行代码不同。 附1135和1584两题Python简洁Prim解法:1135. 最低成本联通所有城市class Solution:
def minimumCost(self, n: int, connections: List[List[int]]) -> int:
graph = [[] for _ in range(n)]
for connection in connections:
graph[connection[0] - 1].append((connection[2], connection[1] - 1))
graph[connection[1] - 1].append((connection[2], connection[0] - 1))
heap = [(0, 0)]
mst = set()
cost = 0
while heap:
cur = heapq.heappop(heap)
if cur[1] not in mst:
mst.add(cur[1])
cost += cur[0]
for vertex in graph[cur[1]]:
heapq.heappush(heap, vertex)
if len(mst) == n:
return cost
return -1 1584. 连接所有点的最小费用class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
n = len(points)
graph = [[] for _ in range(n)]
for i in range(n - 1):
for j in range(i + 1, n):
graph[i].append((abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]), j))
graph[j].append((abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]), i))
heap = [(0, 0)]
mst = set()
cost = 0
while heap:
cur = heapq.heappop(heap)
if cur[1] not in mst:
mst.add(cur[1])
cost += cur[0]
for vertex in graph[cur[1]]:
heapq.heappush(heap, vertex)
if len(mst) == n:
return cost |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
cut({A, B, C}) = cut({A, B}) + cut({C}) - [cut({A,B}) ^cut({C}) ],这样才对吧,减去它俩的交集, 刚写错了 |
Beta Was this translation helpful? Give feedback.
-
cpp1135.最低成本联通所有城市 struct cmp
{
bool operator()(const vector<int> &edge1, const vector<int> &edge2)
{
return edge1[2] > edge2[2];
}
};
class Solution
{
vector<bool> visited;
vector<vector<vector<int>>> graph;
using PQ = priority_queue<vector<int>, vector<vector<int>>, cmp>;
PQ q;
public:
int minimumCost(int n, vector<vector<int>> &connections)
{
// 编号从 1 ~ n 为了直接使用序号,多了一个0节点,需要手动设置为true
visited = vector<bool>(n + 1);
visited[0] = true;
buildGraph(n, connections);
addPointsAndEdges(1);
int sum = 0;
while (!q.empty())
{
auto curEdge = q.top();
int from = curEdge[0], to = curEdge[1], cost = curEdge[2];
q.pop();
// 访问过,说明该点已经连接
if (visited[to])
{
continue;
}
sum += cost;
addPointsAndEdges(to);
}
for_each(visited.begin(), visited.end(), [&sum](bool isVisited)
{
if(!isVisited)
{
sum = -1;
} });
return sum;
}
void buildGraph(int n, vector<vector<int>> &connections)
{
graph = vector<vector<vector<int>>>(n + 1);
for (auto connect : connections)
{
int from = connect[0], to = connect[1], cost = connect[2];
graph[from].emplace_back(vector<int>{from, to, cost});
graph[to].emplace_back(vector<int>{to, from, cost});
}
}
void addPointsAndEdges(int cur)
{
visited[cur] = true;
for (auto nxtEdge : graph[cur])
{
int from = nxtEdge[0], to = nxtEdge[1], cost = nxtEdge[2];
if (visited[to])
{
continue;
}
q.push(nxtEdge);
}
}
}; 1584.连接所有点的最小费用 class Solution
{
using PQ = priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>;
// 表示点的数量
int n;
unordered_set<int> inMst;
// { 点距离, 去向的点}
PQ q;
public:
int minCostConnectPoints(vector<vector<int>> &points)
{
n = points.size();
addPointAndEdges(0, points);
int sum = 0;
while (!q.empty())
{
pair<int, int> curEdge = q.top();
q.pop();
int cost = curEdge.first, to = curEdge.second;
if (inMst.count(to))
{
continue;
}
sum += cost;
addPointAndEdges(to, points);
if (inMst.size() == n)
{
break;
}
}
return sum;
}
void addPointAndEdges(int cur, vector<vector<int>> &points)
{
inMst.insert(cur);
for (int i = 0; i < n; ++i)
{
// 该点已经加入了mst中,就不需要加入了
if (inMst.count(i))
{
continue;
}
int x1 = points[cur][0], y1 = points[cur][1],
x2 = points[i][0], y2 = points[i][1];
int diff = abs(x1 - x2) + abs(y1 - y2);
q.push({diff, i});
}
}
}; |
Beta Was this translation helpful? Give feedback.
-
打卡!理论上Kruskal和Prim时间复杂度一致,为啥leetcode上Prim要比Kruskal慢呢 |
Beta Was this translation helpful? Give feedback.
-
1548题C++中使用prim算法超时了,不知道为啥。希望有大佬帮忙看看 struct cmp
{
bool operator()(const vector<int> &edge1, const vector<int> &edge2)
{
// 按照边的权重从小到大排序
return edge1[2] > edge2[2];
}
};
class Prim
{
private:
// 核心数据结构,存储「横切边」的优先级队列
using PQ = priority_queue<vector<int>, vector<vector<int>>, cmp>;
PQ pq;
// 类似 visited 数组的作用,记录哪些节点已经成为最小生成树的一部分
vector<bool> inMST;
// 记录最小生成树的权重和
int weightSum;
// graph 是用邻接表表示的一幅图,
// graph[s] 记录节点 s 所有相邻的边,
// 三元组 int[]{from, to, weight} 表示一条边
vector<vector<vector<int>>> graph;
/**
* @brief 将 s 的横切边加入优先队列
*
* @param s
*/
void cut(int s)
{
for (auto &edge : graph[s])
{
int to = edge[1];
if (inMST[to])
{
continue;
}
pq.push(edge);
}
}
public:
Prim(const vector<vector<vector<int>>> &graph) : graph(graph)
{
int n = graph.size();
inMST.resize(n);
weightSum = 0;
// 随便从一个点开始切分都可以,我们不妨从节点 0 开始
inMST[0] = true;
cut(0);
// 不断进行切分,向最小生成树中添加边
while (!pq.empty())
{
vector<int> edge = pq.top();
pq.pop();
int to = edge[1];
int weight = edge[2];
if (inMST[to])
{
// 节点 to 已经在最小生成树中,跳过
// 否则这条边会产生环
continue;
}
// 将边 edge 加入最小生成树
weightSum += weight;
inMST[to] = true;
// 节点 to 加入后,进行新一轮切分,会产生更多横切边
cut(to);
}
}
/**
* @brief 最小生成树的权重和
*
* @return int
*/
int getWeightSum()
{
return weightSum;
}
/**
* @brief 判断最小生成树是否包含图中的所有节点
*
* @return true
* @return false
*/
bool allConnected()
{
for (int i = 0; i < inMST.size(); i++)
{
if (!inMST[i])
{
return false;
}
}
return true;
}
}; 1584中代码 class Solution
{
public:
int minCostConnectPoints(vector<vector<int>> &points)
{
int n = points.size();
vector<vector<vector<int>>> graph = buildGraph(n, points);
return Prim(graph).getWeightSum();
}
vector<vector<vector<int>>> buildGraph(int n, vector<vector<int>> &points)
{
vector<vector<vector<int>>> graph(n);
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
int xi = points[i][0], yi = points[i][1];
int xj = points[j][0], yj = points[j][1];
// 用坐标点在 points 中的索引表示坐标点
graph[i].push_back({i, j, abs(xi - xj) + abs(yi - yj)});
graph[j].push_back({j, i, abs(xi - xj) + abs(yi - yj)});
}
}
return graph;
}
}; |
Beta Was this translation helpful? Give feedback.
-
Python中可用 heapq 作為 Priority Queue import heapq
class Solution(object):
def minCostConnectPoints(self, points):
"""
:type points: List[List[int]]
:rtype: int
"""
n = len(points)
graph = self.buildGraph(n, points)
return Prim(graph).weightSum
def buildGraph(self, n, points):
graph = [[] for _ in range(n)]
# 生成所有邊及權重
for i in range(n):
for j in range(i+1, n):
xi, yi = points[i][0], points[i][1]
xj, yj = points[j][0], points[j][1]
weight = abs(xi - xj) + abs(yi - yj)
# 用 points 中的索引表示座標點
graph[i].append((i, j, weight))
graph[j].append((j, i, weight))
return graph
class Prim(object):
def __init__(self, graph):
self.graph = graph
n = len(graph)
self.pq = []
# Visited 數組功用
self.inMST = [False for _ in range(n)]
self.weightSum = 0
# 隨便從一個節點開始分割,不妨從節點 0 開始
self.inMST[0] = True
self.cut(0)
# 不斷進行分割,向最小生成樹中添加邊
while self.pq:
edge = heapq.heappop(self.pq)
# 形式 (priority_order, (node1, node2, weight))
to = edge[1][1]
weight = edge[1][2]
# 節點 to 已經在最小生成樹中,則跳過
# 避免產生 Cycle
if self.inMST[to]:
continue
# 將 edge 加入最小生成樹
self.weightSum += weight
self.inMST[to] = True
self.cut(to)
def cut(self, s):
# 遍歷 s 的鄰邊
for edge in self.graph[s]:
# edge的終點
to = edge[1]
# 若相鄰接點 to 已經在最小生成樹中,跳過
if self.inMST[to]:
continue
# 不在最小生成樹中,則加入優先級隊列(照權重)
# heapq.heappush(heap, (priority order, value))
heapq.heappush(self.pq, (edge[2], edge))
def allConnected(self):
# 判斷最小生成樹是否包含圖中所有節點
for i in range(len(self.inMST)):
if not self.inMST[i]:
return False
return True |
Beta Was this translation helpful? Give feedback.
-
c++的代码好像有问题。 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:Prim 最小生成树算法
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions