最小生成树
1.最小生成树的性质:
- 最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环。
- 对给定的图G(V,E),其最小生成树可以不唯一,但其边权和一定是唯一的。
- 最小生成树是一个无向图,因此其根结点可以是这棵树上任意的一个结点。
2.prim算法
prim算法和Dijkstra算法使用的思想几乎相同,只有在数组dis[]的含义上有所区别。其中,Dijkstra算法的数组dis[]含义为起点s到达顶点Vi的最短距离,而prim算法的数组dis[]含义为顶点Vi与集合S的最短距离,两者的区别在与最短距离是顶点Vi针对”起点s”还是”集合S”。
int prim(int s){
for(int i = 0;i < maxn;i++)
dis[i] = INF;
dis[s] = 0; // 指定 s 为起始点,离集合 S 距离为0
int sum = 0;
for(int i = 0;i < n;i++){
int u = -1,min = INF;
for(int j = 0;j < n;j++){
if(!visit[j] && dis[j] < min){
u = j;
min = dis[j];
}
}
if(u == -1) return -1;
visit[u] = true;
sum += dis[u];
for(int j = 0;j < n;j++){
// j 未访问 && u能到达j && 以 u 为中介点可以使 j 离集合更近
if(!visit[j] && G[u][j] != INF){
if(G[u][j] < dis[j]){
dis[j] = G[u][j];
}
}
}
}
return sum;
}
3.kruskal算法
kruskal算法采用了边贪心的策略,执行下面的步骤:
- 对所有边按边权从小到大进行排序。
- 按边权从小到大测试所有边,如果当前所连接的两个顶点不在同一个连通块中(并查集),则把这条测试边加入当前最小生成树,否则,舍去。
- 执行步骤2,直到最小生成树中的边数等于总顶点数减一或是测试完所有边时结束。而当结束时如果最小生成树的边数小于总顶点数减一,说明该图不连通。
struct edge{
int u,v;
int weight;
}E[maxn];
bool cmp(edge e1,edge e2){
return e1.weight < e2.weight;
}
int findFather(int x){//并查集查询函数
int a = x;
while(x != father[x]){
x = father[x];
}
//路径压缩
while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
int kruskal(int n,int m){
for(int i = 0;i < n;i++)
father[i] = i;
int sum = 0,numEdge = 0;
//按边权排序,边权小的优先
sort(E,E+m,cmp);
for(int i = 0;i < m;i++){ //枚举所有的边
int v1 = findFather(E[i].u);
int v2 = findFather(E[i].v);
if(v1 != v2){ //如果不是同一个父结点,证明不在同一个集合中,将两者合并
father[v1] = v2; //合并
sum += E[i].weight;
numEdge++; //当前生成树边数加一
if(numEdge == n - 1) break; //边数等于顶点数减一时,表示所有的边都加入进来了,结束算法
}
}
if(numEdge != n - 1)
return -1 //无法连通时返回 -1
return sum;
}