最小生成树(Minimum Spanning Tree)
生成树的定义
在了解最小生成树之前,让我们先了解生成树的定义:
一个连通图的生成树是一个极小的连通子图,它包含图中全部的n个顶点,但只有构成一棵树的n-1条边。
例如一个包含3个顶点的完全图可以产生3棵生成树。对于包含n个顶点的无向完全图最多包含 n ^ (n-2) 棵生成树。
最小生成树定义
所谓一个带权图的最小生成树,就是原图中边的权值最小的生成树 ,所谓最小是指边的权值之和小于或者等于其它生成树的边的权值之和。
如图,边上的数字即为各连通路间的权值,可以简单理解为两点间距离,从B到C的最短权值,在图中可以看出,为B->A->C,总权值为3,优于B->C的权值为4的通路。
所以最小生成树的定义便显而易见了:生成树的权值之和最小,且包含原图中的所有顶点。
那么,怎么求最小生成树呢,下面介两种算法:Prim算法和Kruskal算法
Prim算法
算法描述
Prim算法从某一个起点出发,找到该起点到权值最小的未被访问的点,标记为访问,更新各点间权值,并将其作为下一个起点,重复上一步骤, 具体过程如下:
过程
- 输入:一个加权连通图,其中顶点集合为V,边集合为E;
- 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
- 重复下列操作,直到Vnew = V:
a. 在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b. 将v加入集合Vnew中,将<u, v>边加入集合Enew中;- 输出:使用集合Vnew和Enew来描述所得到的最小生成树。
邻接表实现
以邻接表为例,以下为Prim算法的C++代码实现
#include <iostream>
using namespace std;
const int inf = 0x3f3f3f3f, MAX = 1e5;
class Prim{
private:
int adjvex[MAX]; // 邻接表
int dis[MAX]; // 起点到各点的权值
bool vis[MAX]; // 标记是否访问
int arc[MAX][MAX];// 存储各点间距离 若不连通则为inf
public:
void MiniSpanTree(int n){
// 以0为起点
adjvex[0] = 0;
dis[0] = 0;
// 初始化
for (int i = 0; i < n; i++){
dis[i] = arc[0][i];
adjvex[i] = 0;
}
// 构造最小生成树
for (int i = 1; i < n; i++){
// 找到该起点到权值最小的未被访问的点
int minv = inf, k = 0;
for (int j = 1; j < n; j++){
if (!vis[j] && dis[j] < minv){
minv = dis[j];
k = j;
}
}
// 输出路径
cout