基本概念:
在无向图中,连通且不含圈的图称为 “树” 。
给定无向图G=(V,E), 连接G中所有点,且边集是E的子集的树称为G的 “生成树” 。
在G的所有生成树中,权值最小的那颗树称为 “最小生成树” 。
构造MST的算法有很多,最常见的有两个 : Kruskal算法 和 Prime算法 。
Kruskal算法:(适用于稀疏图)
原理:
1. 将图中所给出的所有边按照从小到大的顺序排列,然后依次考察每条边(u,v)。
2.
情况1. u和v在同一个连通分量中,那么加入(u,v)后会形成环,因此不能选择。
情况2. u和v在不同的连通分量中,那么就加入(u,v)。
相关算法: 并查集
并查集: 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。
模板代码如下:
int u[MAX],v[MAX],w[MAX]; //u[i]表示一条边的起点,v[i]表示一条边的终点,w[i]表示改变的权值,i表示该边的编号。
int cmp(const int i,const int j) //间接排序函数
{
return w[i]<w[j];
}
int find(int x) //并查集的find
{
return p[x]==x?x:find(p[x]);
}
int Kruskal()
{
int ans=0;
for(int i=0;i<n;i++) p[i]=i; //初始化并查集
for(int i=0;i<m;i++) r[i]=i; //初始化边序号
sort(r,r+m,cmp); //给边排序
for(int i=0;i<m;i++)
{
int e=r[i];
int x=find(u[e]);
int y=find(v[e]); //找出当前边两个端点所在集合编号
if(x!=y) // 如果在不同集合,就合并
{
ans+=w[e]; p[x]=y;
}
}
return ans;
}
Prime算法: (适合稠密图)
prime算法和边数无关,只和顶点数量有关。
基本原理:
1. 将一个图的顶点分为两部分,一部分是最小生成树中的结点(A集合),另一部分是未处理的结点(B集合)。
2. 首先选择一个顶点放入集合A中,然后遍历集合B中的所有顶点,找出与A中顶点关联的所有边中权值最小的一条边(顶点为v),将v从B集合中删除并加入A集合。
3.递归重复步骤2,直到B集合中的顶点为空,结束此过程。
模板代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define INF 0xffffff
#define MAXN 110
int map[MAXN][MAXN], lowcost[MAXN];
bool visit[MAXN];
int nodenum, sum;
void prim()
{
int temp, k;
sum = 0;
memset(visit, false, sizeof(visit)); //初始化visit
visit[1] = true;
for(int i = 1; i <= nodenum; ++i) //初始化lowcost[i]
lowcost[i] = map[1][i];
for(int i = 1; i < nodenum; ++i)//找生成树集合点集相连最小权值的边
{
temp = INF;
for(int j = 1; j <= nodenum; ++j)
if(!visit[j] && temp > lowcost[j])
temp = lowcost[k = j];
if(temp == INF) break;
visit[k] = true; //加入最小生成树集合
sum += temp; //记录权值之和
for(int j = 1; j <= nodenum; ++j) //更新lowcost数组
if(!visit[j] && lowcost[j] > map[k][j])
lowcost[j] = map[k][j];
}
}