先来说说什么是树。
树实际上是图的一种,当一个有N个点的无向连通图,只有N-1条边时,就是一棵树,即树中不会有环出现;所以对于一个图,删除某些环中的某条边,使得该图成为一棵树,那么这棵树就称为生成树。
而最小生成树的意思就是,给定有n个顶点的带权图G(E,V),找到一棵生成树,求该生成树的边权和。
Kruskal算法:
算法步骤:
1.构造一个有n个顶点的无边子图;
边权最小的边加入该子图,直至子图成为一棵树;
3.边能加入子图的条件是,边的两个端点u,v还未连通,Kruskal算法中运用并查集的查询来询问两个顶点是否连通;
Kruskal算法的本质是,通过树的合并(不断加边,构成子树),来构建完整的生成树。
Kruskal+邻接表 模板如下:
//复杂度O(ElogE),E为边数
const int INF=10e8;
const int MAXN=110;
const int MAXM=MAXN*MAXN;
struct edge
{
int u,v,cost;
bool operator < (const edge &a)const
{
return cost<a.cost;
}
};
edge E[MAXN];
int couEdge; //记得初始化couEdge=0,然后进行加边操作
int fa[MAXN];
int Find(int x)
{
if(fa[x]==-1) //important!!
return x;
fa[x]=Find(fa[x]);
return fa[x];
}
int Kruskal(int n)
{
int ans=0;
int cou=0; //important!! 建好子图之后,每给子图加上一条边,cou+1
int u,v,cost,t1,t2;
memset(fa,-1,sizeof(fa)); //important!!
sort(E,E+couEdge);
for(int i=0;i<couEdge;i++)
{
u=E[i].u;v=E[i].v;cost=E[i].cost;
t1=Find(u);t2=Find(v); //t1,t2分别代表u,v的父节点
if(t1!=t2)
{
ans+=cost;
fa[t1]=t2;
cou++;
}
if(cou==n-1)
break;
}
if(cou<n-1)
return -1; //表示该图不连通
return ans;
}
//加边操作
void add_edge(int u,int v,int cost)
{
E[couEdge].u=u;E[couEdge].v=v;E[couEdge].cost=cost;
}
Prim算法:
前面说了,Kruskal算法是通过树的合并来构建生成树,而现在要说的Prim算法,其本质则是,从一个顶点扩展成生成树,复杂度O(N2)。
【对比:Kruskal和Prim的异同】
同:都是贪心思想;都是选择边权最小的边;
异:Kruskal是优先选择边权最小的边,Prim是从点集出发选择边权最小的边;
//当题目中边的数量比顶点多的时候,用Prim速度比较快。
Prim算法的基本步骤:
1.初始化点集 V={x};
u∈点集V,v不∈点集V;
满足条件的边中最小的一条加入生成树,并把v加入点集V,重复执行,直至原图所有的点都加入点集V,就得到了一棵最小生成树。
Tips:关于如何快速找到可以添加到生成树的边:
可以维护一个数组lowcost[i…j]记录点集V到各个顶点的最小边,即可快速找到边,且每当有新的点加入点集V时,该数组都要更新一次。
Prim+邻接矩阵 模板如下:
const int INF=10e8;
const int MAXN=110;
bool vis[MAXN];
int lowcost[MAXN];
int Prim(int cost[][MAXN],int n)
{//输入cost[][],n;且cost[][]的初始化要注意没连通的边存INF
int ans=0;
int minn,k;
memset(vis,0,sizeof(vis));
vis[1]=1; //从点1开始扩展,找距离1最小的边扩展
for(int i=1;i<=n;i++)
lowcost[i]=cost[1][i]; //!
for(int i=1;i<=n-1;i++) //循环n-1次,因为vis[1]=1
{
minn=INF;k=-1;
for(int j=1;j<=n;j++)
if(!vis[j]&&minn>lowcost[j])
{
minn=lowcost[j];
k=j;
}
if(k==-1) break; //不连通
ans+=minn;vis[k]=1; //再从k点开始扩展,准备更新lowcost[]
for(int j=1;j<=n;j++)
if(!vis[j]&&lowcost[j]>cost[k][j])
lowcost[j]=cost[k][j];
}
return ans;
}