1.问题
如何求一个包含有原图所有n个结点的且所有边的代价和最小的极小连通子图。
2.解析
构造最小生成树有两种算法模式。
第一种是Kruskal算法。它的要点就是选边。即从最短的边开始生成森林,最后连成树。简单的步骤:
1.图中的所有边按权重从小到大排序;
2.图中的n个顶点看成独立的n棵树组成的森林;
3.按权重从小到大选择边,所选的边连接的两个顶点应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
4.重复3,直到所有顶点都在一颗树内或者有n-1条边为止。
以下面为例。所有边按照权重排序为1.1.2.2.4.4.5.6.7.8.10,每一个顶点为独立的森林。然后一步步根据权重的顺序选择边。第一步选择v1和v4连接的边,权重为1。第二步选择v6和v7连接的边,权重为1。以此类推,等到选择第二条权重为4的边时,发现连接后会形成闭环或者说是两个顶点都被选中过,就不选择,跳到下一个边去。一直循环往复,直到所有顶点都在一颗树内或者有n-1条边为止。
第二种是prim算法。它的要点就是选点。即从某一个点出发,在两个点之间选择权重最小的一条边,连接两个点,加入生成树之中。简单的步骤:
1.图的所有顶点集合为VV。
2.在顶点集合中能够组成的边中,选择一条代价最小的边,加入到最小生成树中。
3.重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
以下图为例,先选择v1点,在v1的邻接点中选择权重最小的v4。然后再v1和v4的共同邻接点中继续选择权重最小的点,即v2或v3,这里选择了v2。然后在三者的并查集中再次选择,即v3。继续重复上述流程,直到最小生成树有n-1条边或者n个顶点为止。
3.设计 1.1 void kruskal(edge e[],int m,int n){ int i,j,v1,v2,s1,s2,k,sum=0; int set[m+1]; //构建顶点编号集 for(i=1;i<=m;i++){ set[i]=i; } //最小生成树的第k条边 k=1; //边的下标 j=0; while(k<=n){ if (j>n) break; //先获取j边的邻接点,以及所属的集合编号 v1=e[j].v1; v2=e[j].v2; s1=set[v1]; s2=set[v2]; //当两点的集合不同时,表示该边为生成树的一条边 if(s1!=s2){ printf(“V%d-V%d=%d\n”,v1,v2,e[j].w); sum+=e[j].w; k++; //最小生成树的边数增加 if(k>=n) break; for(i=1;i<=m;i++){ if(set[i]==s2) set[i]=s1; //表示该点已经加入v1的集合中,同一集合 } } j++; //下一条边 } printf(“最小权值之和=%d\n”,sum);} 1.2
2.1 void prim(int graph[][MAX],int n){ int mincost[MAX]; int mst[MAX]; int i,j,min,minid,sum=0; for(i=2;i<=n;i++){ //以顶点1为起始点,并存放顶点1到邻接点的路径。 mincost[i]=graph[1][i]; mst[i]=1; } mst[1]=0; for (i=2;i<=n;i++){ min=MAXCOST; minid=0; for(j=2;j<=n;j++){ //找出权重最短的路径和id if(mincost[j]<min&&mincost[j]!=0){ min=mincost[j]; minid=j; } } printf(“V%d-V%d=%d\n”,mst[minid],minid,min); //对权重求和 sum+=min; //对最短路径的权重置0; mincost[minid]=0; for(j=2;j<=n;j++){ //路径更新 if(graph[minid][j]<mincost[j]){ mincost[j]=graph[minid][j]; mst[j]=minid; } } } printf(“最小权重之和=%d\n”,sum);} 2.2
4.分析
因为prim算法需要选点,通过遍历所有点集选出权重小的路径,并加入另一个点,再次遍历并查集,所以时间复杂度与顶点数v有关。Prim算法的复杂度为T=O(|V|^2),适用于边数较多的稠密图
因为kruskal算法需要选边,通过先对边的权重进行升序排序,,在升序遍历权重对应的点,加入没有被选择过的点,所以时间复杂度与边数e有关。Kruskal算法的复杂度为T = O(|E|log|E|) 用于边数较少的稀疏图
5.源码
https://github.com/Chenzh0205/Algorithm/tree/main/%E4%BD%9C%E4%B8%9A1
6.参考资料
1.数据结构课程ppt
2.最小生成树构造算法–Prim算法,Kruskal算法(C语言)
极速赛车买前5名的方法raph[][MAX],int n){ int mincost[MAX]; int mst[MAX]; int i,j,min,minid,sum=0; for(i=2;i<=n;i++){ //以顶点1为起始点,并存放顶点1到邻接点的路径。 mincost[i]=graph[1][i]; mst[i]=1; } mst[1]=0; for (i=2;i<=n;i++){ min=MAXCOST; minid=0; for(j=2;j<=n;j++){ //找出权重最短的路径和id if(mincost[j]<min&&mincost[j]!=0){ min=mincost[j]; minid=j; } } printf(“V%d-V%d=%d\n”,mst[minid],minid,min); //对权重求和 sum+=min; //对最短路径的权重置0; mincost[minid]=0; for(j=2;j<=n;j++){ //路径更新 if(graph[minid][j]<mincost[j]){ mincost[j]=graph[minid][j]; mst[j]=minid; } } } printf(“最小权重之和=%d\n”,sum);} 2.2
4.分析
因为prim算法需要选点,通过遍历所有点集选出权重小的路径,并加入另一个点,再次遍历并查集,所以时间复杂度与顶点数v有关。Prim算法的复杂度为T=O(|V|^2),适用于边数较多的稠密图
因为kruskal算法需要选边,通过先对边的权重进行升序排序,,在升序遍历权重对应的点,加入没有被选择过的点,所以时间复杂度与边数e有关。Kruskal算法的复杂度为T = O(|E|log|E|) 用于边数较少的稀疏图
5.源码
https://github.com/Chenzh0205/Algorithm/tree/main/%E4%BD%9C%E4%B8%9A1
6.参考资料
1.数据结构课程ppt
2.最小生成树构造算法–Prim算法,Kruskal算法(C语言)