V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

Prim 最小生成树

图算法 · 最小生成树(MST)

什么是最小生成树

在一张无向带权连通图里,选若干条边把所有点连起来、不成环、总权重最小——就是最小生成树(MST)。Prim 和 Kruskal 是求它的两大经典算法,思路不同、结果一致。

Prim 怎么做

Prim 从一个起点开始,把它当作一棵只有一个点的树,然后让树一点点长大:每一步,在所有「一端在树里、一端在树外」的横切边里挑权重最小的那条,把它和它连接的树外点并入树。重复 V−1 次,就把所有点接进来了。

下面固定一张 6 个点的无向带权图,从 A 起。点「下一步」逐步看:当前选中的最小横切边黄色高亮,加入后变绿、新点也并进绿色的树。走到底得到 5 条边、总权重最小的生成树。右侧代码随每一步同步高亮。

123456789ABCDEF

从起点 A 开始,树里只有它一个点

1function prim(edges: [number, number, number][], n: number, start: number): number {
2 const inTree = Array(n).fill(false);
3 inTree[start] = true;
4 let weight = 0, count = 1;
5 while (count < n) {
6 let best: [number, number, number] | null = null;
7 for (const [u, v, w] of edges) {
8 if (inTree[u] !== inTree[v] && (best === null || w < best[2])) best = [u, v, w];
9 }
10 if (best === null) break;
11 const nv = inTree[best[0]] ? best[1] : best[0];
12 inTree[nv] = true;
13 weight += best[2];
14 count++;
15 }
16 return weight;
17}
起点A
树内点A
当前横切边-
已选边0/5
MST 权重0
1 / 12

为什么每步「贪心取最小横切边」是对的?由切分定理保证:把已在树里的点和其余点看成一种分割,横跨它的最小边一定属于某棵最小生成树。用堆维护候选横切边,复杂度约 O(E log V)——在稠密图上通常比 Kruskal 更省。

Prim vs Kruskal:同一问题,两种思路

Prim(本页):从一个点生长,每步选「树↔树外」的最小横切边——始终维护一棵连通的树。稠密图更优。
Kruskal:把边按权全局排序,从小到大逐条尝试,用并查集判环——过程中是一片森林,最后才连成一棵。稀疏图更优。
两者在同一张图上得到相同的 MST(本页与 Kruskal 页正是同一张图,可对照:边集一样、加入顺序不同)。

想看按权选边 + 并查集判环的另一种建法,去看 Kruskal 最小生成树;判环背后的并查集,可回看 数据结构 · 并查集。