1)最小生成树
给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫生成树。如果边上有权值,那么使得边权和最小的生成树叫做最小生成树(MST,Minimum Spanning Tree)
2)应用
比如让你为一个镇的九个村庄架设通信网络,每个村庄相当于一个顶点,权值是村与村之间可通达的直线距离,要求你必须用最小的成本完成这次任务;或者村庄之间建公路,连通N个村庄要N-1条路,如何让建路成本最低之类的问题。
1、Prim算法
①该算法是构建最小生成树的算法之一。它是以某顶点为起点,不断添加各顶点上最小权值的边,来构建最小生成树。
②算法流程:
第一步:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V;
第二步:从集合U中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;
以此类推,现在的集合V={a,b},再从集合U中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V;直至所有顶点全部被加入V,此时就构建出了一棵MST。
③参考代码:
#include <iostream> #include <string.h> #define INT_MAX 1000000000 using namespace std; int matrix[100][100];//邻接矩阵 bool visited[100];//标记数组 int path[100];//记录生成树路径 int lowcost[100];//边的权值 int vertex_num,arc_num;//顶点数,弧数 int sum;//权值总和 int source;//源点 void prim(int source); int main() { cout << "请输入图的顶点数(<=100):"; cin >> vertex_num; cout << "请输入图的弧数:"; cin >> arc_num; for (int i = 0; i < vertex_num; i++) for (int j = 0; j < vertex_num; j++) matrix[i][j] = INT_MAX; //初始化matrix数组 cout << "请输入弧的信息: "; int u, v, w; for (int i = 0; i < arc_num; i++) { cin >> u >> v >> w; matrix[u][v] = matrix[v][u] = w; } cout << "请输入起点(<" << vertex_num << "):"; cin >> source; prim(source); cout << "最小生成树权和为:" << sum << endl; cout << "最小生成树路径为: "; for (int i = 0; i < vertex_num; i++) if (i != source) cout << i << "----" << path[i] << endl; return 0; } void prim(int source){ memset(visited,0,sizeof(visited)); visited[source]=true; for(int i=0;i<vertex_num;i++){ lowcost[i] = matrix[source][i]; path[i]=source; } int min_cost,min_cost_index;//最小的权值,和其下标 sum=0; for(int i=1;i<vertex_num;i++){//找除源点外的n-1个点,如果这里写多了, //那么下边的for里if进不去,那么sum的值也会错误 min_cost=INT_MAX; for(int j=0;j<vertex_num;j++){//遍历所有顶点 if(visited[j]==false&&lowcost[j]<min_cost){//找到与源点的权值最小的点 min_cost=lowcost[j]; min_cost_index=j; } } visited[min_cost_index]=true;//讲找到的顶点进行标记 sum+=min_cost;//权值总和 for(int j=0;j<vertex_num;j++){ if(visited[j]==false&&matrix[min_cost_index][j]<lowcost[j]){//更新lowcost,以便找下个顶点 lowcost[j]=matrix[min_cost_index][j]; path[j] = min_cost_index; } } } }
摘自http://www.61mon.com/index.php/archives/199/comment-page-1#comments
2、Kruskal算法
①该算法是构建最小生成树的另一个算法,其是对边进行操作,来构建最小生成树的。
②算法流程:
第一步:把所有的边按权值从小到大排列。
第二步:按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并。
重复第二步,直到 所有的点都属于同一个集合。
第二步用并查集来实现,又因为每次都选的权值最小的,所以这其实就是运用并查集的贪心算法。
③为什么这样做就可以构建最小生成树呢?因为N个顶点只需要N-1条边就够了,那么选哪N-1条呢,为了让权值和最小,当然选从小到大排序的前N-1条边喽。
④参考代码:
#include <iostream> #include <algorithm> #define MAX_INT 100 using namespace std; struct Edge{//边 int a;//边上的两个点 int b; int weight;//权值 }edge[MAX_INT]; int par[MAX_INT];//i的父亲的编号 int rank[MAX_INT];//i的高度 int vertex_num,arc_num;//顶点数,弧数 int case_num;//测试组数 int sum;//权值总和 void init(int n);//初始化 int find(int x);//查找根节点并且压缩路径 bool unite(int x,int y);//合并 bool compare(const Edge&a,const Edge&b); int main() { cout<<"请输入测试组数:"<<endl; cin>>case_num; while(case_num){ sum=0; cout<<"请输入顶点数和弧数:"<<endl; cin>>vertex_num>>arc_num; init(vertex_num); cout<<"请输入"<<arc_num<<"条弧的信息:"<<endl; for(int i=0;i<arc_num;i++){ cin>>edge[i].a>>edge[i].b>>edge[i].weight; } sort(edge,edge+arc_num,compare); cout<<"最小生成树的路径为:"<<endl; int j; for(j=0;j<arc_num;j++){ if(unite(edge[j].a,edge[j].b)){ sum+=edge[j].weight; cout<<edge[j].a<<"----"<<edge[j].b<<" "<<edge[j].weight<<endl; } } if(j==arc_num){ cout<<"最小生成树的权值和为:"<<sum<<endl; }else if(j<arc_num){ cout<<"data error!"<<endl; } case_num--; } return 0; } void init(int n){ for(int i=0;i<n;i++){ par[i]=i;//父节点都先初始化为自己 rank[i]=0;//高度初始化为0 } } int find(int x){ if(x==par[x]){ return x; }else{ return par[x]=find(par[x]); } } bool unite(int x,int y){ x=find(x); y=find(y); if(x==y){ return false; }else{ if(rank[x]<rank[y]){ par[x]=y; }else{ par[y]=x; if(rank[x]==rank[y]) rank[x]++; } return true; } } bool compare(const Edge&a,const Edge&b){//按从小到大的顺序排序 return a.weight<b.weight; }
代码参考自:http://blog.csdn.net/niushuai666/article/details/6689285
3、Kruskal算法和Prim算法的区别(或者说什么时候用Prim什么时候用Kruskal)?
Prim算法时间复杂度为,与网中的边数无关,适用于求边稠密的网的最小生成树。
Kruskal算法时间复杂度为,适用于求稀疏的网的最小生成树。
。