一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。简单来说就是假如有10个点,你可以用9条线把这10个点连起来,不漏掉任何一个点,然后这9条边的权值最小,就是**最小生成树**了,就是用*小于顶点个数-1的边*,连接所有点,权值最小。
最小生成树可以用kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。
Prim算法和Kruskal算法都是基于下述性质:如果(u,v)是图中权值最小的边,则最小生成树中必包含此边。Kruskal算法是针对边来说的,而Prime算法是针对顶点的。
分析Prim算法,该算法由两个并列的循环组成,第一个循环次数为vex_num(即顶点的个数n);第二个循环,外层循环的次数为n-1,内层的循环次数为n。所以总体来说,Prim的时间复杂度为O(n2),并且该算法与图中边数的多少无关,所以该算法适合于求边稠密的图的最小生成树。是一种贪心算法 。
Kruskal算法:将图各边按照权值进行排序,之后每次选择一条边,要注意边不能构成回路。
现在只学会了Kruskal算法,Prim还不会,不过只会Kruskal也是可以解决问题的( • ̀ω•́ )✧
这位大牛有讲Prim算法的,可以看一看Prim:http://blog.csdn.net/niushuai666/article/details/6689295
大概框架是:
图中节点的存储
struct edge{
int u,v,w; //边的顶点,权值
}edges[10];
节点权值排序函数
qsort(edges, m, sizeof(edge), cmp)
合并两个节点函数
void merge(int a,int b)
具体一些的代码:还是要用到之前的并查集的知识 并查集:http://blog.csdn.net/rricky_/article/details/76576120
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<algorithm>
using namespace std;
int parent[10];
int n,m,num,sumWeight;
int i,j;
struct edge{
int u,v,w; //边的顶点,权值
}edges[10];
//初始化并查集
void UFset(){
for(i=1; i<=n; i++) parent[i] = -1;
}
//查找i的根
int find(int x)
{
if(x!=parent[x])
parent[x]=find(parent[x]);
else return parent[x];
}
//合并两个元素a,b
void join(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
parent[fx]=fy;
printf("加入边:%d %d,权值: %d\n", x,y,edges[i].w);
sumWeight += edges[i].w;
num ++;
}
}
void kruskal(){
sumWeight = 0;
num = 0;
int u,v;
UFset();
for(i=0; i<m; i++)
{
u = edges[i].u;
v = edges[i].v;
join(u,v);
if(num==n-1)
{
printf("最小生成树的权值之和为:%d \n", sumWeight);
break;
}
}
}
//比较函数,用户排序
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int main() {
scanf("%d %d", &n, &m);
for(i=0; i<m; i++){
scanf("%d %d %d", &edges[i].u, &edges[i].v, &edges[i].w);
}
sort(edges,edges+m,cmp);
kruskal();
}
/*
测试数据:
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
*/