数据结构(构造连通网的最小生成树)

普利姆算法算法

  形象问题:几个村庄之间有N条路,要再路边修下水管道,求在那些路上修管道能在所有村庄连通的基础上使修的管道最短数组

  中心思想:从一个顶点逐渐链接到所有顶点;在链接过程当中找权最小的边加入生成树中spa

方法code

  • 规定从第一个结点出发,找到链接的边中权最小的
  • 将这条边加入生成树中
  • 循环①,②步,不断找到链接一个顶点的权最小的边,直到链接玩所有顶点
  • 顶点和这些边就构成了连通网的最小生成树

 1 //G是一个图结构体:
 2 //numVertexes:顶点个数
 3 //arc:邻接矩阵
 4 void MinSpanTree_prim(MGraph G){
 5     int min , i , j , k;
 6     int adjvax[MAXVEX];//到该位置对应顶点的最短路径的边的另外一个顶点【方便找到边后打印这条边的两个顶点下标】
 7     int lowcost[MAXVEX];//链接到下标对应顶点的边的最小权【为0表示已经找到;INFINITY表示还没找到;其余表示:目前为止的值】
 8             //既然说是目前为止,是由于之后链接到其余顶点,还会添加可选择的边;在这些新加的边中可能会刷新链接它的边的权最小记录
 9 
10     lowcost[0]=0;//第一个顶点做为最小生成树的根,权直接为0
11     adjvax[0]=0;//第一个顶点先加入
12 
13     //初始化操做
14     for(i=1;i<G.numVertexes;i++){
15         lowcost[i]=G.arc[0][i];//将邻接矩阵第0行全部权值加入数组(从第一个顶点开始到其余顶点的权)
16         adjvax[i]=0;//初始化所有为第一个顶点的下标
17     }
18 
19     //开始构建最下生成树
20     for(i=1;i<G.numVertexes;i++){//链接N个顶点,找N-1条边,分别从一个顶点找到其余N-1个顶点
21         min = INFINITY;//记录最小权值;初始化为65535;
22         j=1;//循环使用的索引值
23         k=0;//记录此次链接到的顶点的下标
24 
25         //遍历所有顶点
26         while(j<G.numVertexes){//找到可链接的最小权
27             if(lowcost[j]!=0 && lowcost[j]<min){
28                 min=lowcost[j];
29                 k=j;//将找到的最小权值下标存入k;
30             }
31             j++;
32         }
33 
34         printf("(%d,%d)  ",adjcex[k],k);//打印当前顶点边中权值最小的边;adjcex[k],边的起始下标;k,当前位置
35         lowcost[k]=0;//将当前顶点的权值设置为0,表示此顶点已经链接到生成树中,继续进行链接下一个顶点
36 
37         //邻接矩阵k行逐个遍历所有顶点
38         for(j=1;j<G.numVertexes;j++){
39             if(lowcost[j]!=0 && G.[k][j] < lowcost[j]){
40                 lowcost[j]=G.arc[k][j];//刚刚链接的顶点下标k,链接到j位置顶点的权小于原先链接到j位置的权;那么就刷新链接他的最小权
41                 adjvax[j]=k;//将刷新链接j位置最小权的顶点的下标记录到adjvax数组j位置下;利用该值打印这条权最小记录的边:(adjvax[k],k)
42             }
43         }
44     }
45 }

克鲁斯卡尔算法blog

  直接去找权最小的边来构建生成树(构建过程当中只要避免构成环便可)排序

方法索引

  • 将图中全部边以权排序成边集,图中顶点去掉所有边(构成树林)
  • 遍历边集数组,检测每条边依附的两顶点是否在同一颗树中【不在的话链接两顶点(树)组成一颗树;若在链接后就会构成环,因此舍弃这种边】
  • 遍历完边集数组时就构造出了最小生成树

 1 int find(int *parent,int f){
 2     while(parent[f]>0){
 3         f=parent[f];
 4     }
 5     return f;
 6 }
 7 //parent数组:存储当前位置对应顶点在一棵树上的另外一个顶点的对应下标
 8     //因此刚开始时,存储下标对应的顶点是与自身位置对应顶点是直接相连的
 9     //当这个位置构建最小生成树会直接相连一个顶点以上时,那么下一个链接顶点下标会存储在第一个链接下标顶点对应位置
10     //例如:0下标顶点构建最小生成树时会链接1下标和3下标两个顶点对应位置顶点;那么:parent[0]=1;parent[1]=3;或者parent[0]=3;parent[3]=1
11 //总之parent数组中将同一颗树顶点的下标利用相似链表的方法链接在一块儿;
12     //给f传入一颗树任意顶点对应的下标返回的值(x)都是同样的;而且在parent[x]这个位置是存放这颗树下一次新加的顶点下标的
13 
14 //Kruskal算法生成最小生成树
15 void MiniSpanTree_Kruskal(MGraph G){
16     int i,n,m;
17     Edge edges[MAGEDGE];//定义边集数组
18     int parent[MAGEDGE];//定义parent数组用来判断边与边是否造成环路
19 
20     for(i=0;i<G.numVertexes;i++){
21         parent[i]=0;
22     }
23     for(i=0;i<G.numEdges;i++){
24         //edges:边集数组;边的结构:begin,起点;end,终点;weight,这条边的权
25         //n获得的是edges[i].begin这个下标所属树下一次新加顶点下标在prent数组中存储位置
26         //m获得的值也有edges[i].end顶点所属树的相同做用
27         //因此m==n就说明两顶点属于同一颗树(已经间接链接了,不须要再重复直接链接了;也就是不要造成环路)
28         //只有所属不一样树时才同时有添加的能力,合并两棵树至关于要用掉一棵树的添加能力,合并后的数的添加能力就要原先被添加树承担
29         n=find(parent,edges[i].begin);
30         m=find(parent,edges[i].end);
31         if(n!=m){//若是n==m,则造成环路
32             parent[n]=m;//将此边的结尾顶点放入下标起点的parent数组中,表示此顶点已经在生成树集合中
33             printf("(%d,%d)%d  ",edges[i].begin,edges[i].end,edges[i].weight);
34         }
35     }
36 }