图之Kruskal算法实现-----最小生成树

算法思想:

把n个顶点看成n棵分离的树(每棵树只有一个顶点),每次选取可连接两个分离树中权值最小的边,把两个分离的树合并成一个新的树取代原来的两个分离树,重复n-1步后得到最小生成树.

1.myedge结构体数组存放所有的边及其权值,mytree结构体数组中存放生成的最小生成树的边,vex数组中存放该节点的父节点,初始值为都为0,代表每个节点的父节点都是本身.

2遍历图将边存放在myedge结构体数组中,然后根据权值从小到大排序.

3.选择权值最小的边所对应的权值在数组中的下标分别为p1,p2,然后分别求下标p1,p2所对应节点是否在一棵树上,如果不在,就将p1,p2所对应的节点连接在一起,将其加入mytree中,并更新p1所对应节点的父节点为p2所对应的节点,如果p1,p2在一颗树上,则找下一个组权值继续上述操作,直到myedge数组遍历完毕,mytree中就是我们要求的最小生成树.

下面是我实现的代码,图的建立代码之类的我就不贴出来了,和上一篇的一样,主要说一下KrusKal算法吧,先看代码把

typedef struct{
    char start;
    char end;
    int weight;
}Edge[MAXVEX],EdgeNode;
void sort(Edge myedge,int num)    //对每条边根据权值进行排序
{
    int i,j;
     EdgeNode p;   
    for(i=1;i<=num;i++){
        for(j=i+1;j<num;j++){
            if(myedge[i].weight > myedge[j].weight){
                p=myedge[i];
                myedge[i]=myedge[j];
                myedge[j]=p;
            }
        }
    }
}


int getPosition(AdjList *G,char c)    //获得该节点在图顶点集合中的位置(数组下标)
{
    int i;
    for(i=1;i<=G->vexnum;i++){
        if(G->vertex[i].vexdata == c){
            return  i;
        }
    } 
}
int getEnd(int vex[],int i)        //查看下标为i的节点当前的父节点;
{
    while(vex[i]!=0){
        i=vex[i];
    }
    return i;
}
void  Kruskal(AdjList *G)
{
    int i,p1,p2,index=1;
    int k=1;
    ArcNode *p;
    int vex[MAXVEX];
    Edge myedge,mintree;
    for(i=1;i<=G->vexnum;i++)
    {
        visited[i]=0;
        vex[i]=0;
    }
    for(i=1;i<=G->vexnum;i++){
        visited[i]=1;            //因为是无向图,邻接表中既存了AB,也会存BA,所以当A点遍历后,以A为尾的节点都将重复,不会加在myedge数组中;
        p=G->vertex[i].head->next;
        for(p;p!=NULL;p=p->next){
           //printf("%d ",p->weight);
            if(visited[p->adjvex]!=1){
                myedge[k].start=G->vertex[i].vexdata;
                myedge[k].end=G->vertex[p->adjvex].vexdata;
                myedge[k].weight=p->weight;
                k++;
            }
        }
    }
    sort(myedge,G->arcnum);
    for(i=1;i<=G->vexnum;i++)
    {
        p1=getPosition(G,myedge[i].start);
        p2=getPosition(G,myedge[i].end);
        int m1=getEnd(vex,p1);
        int m2=getEnd(vex,p2);
        if(m1!=m2){      //当m1!=m2时,说明m1和m2没有在一颗树上,可以作为最小生成树的一条边。
            vex[m1]=m2;
            mintree[index++]=myedge[i];
        }
     }
        for(i=1;i<index;i++){
            printf("%c %c %d\n",mintree[i].start,mintree[i].end,mintree[i].weight);
        }
}

比如说有一个图的信息为

5 7 A B C D E AB 10 AE 9 AD 11 EC 21 CD 2 BC 3 BD 7

则生成的邻接表为:


A: 2 10->4 11->5 9->NULL
 B: 1 10->3 3->4 7->NULL
 C: 2 3->4 2->5 21->NULL
 D: 1 11->2 7->3 2->NULL
 E: 1 9->3 21->NULL

边排序后得到:

C D 2
B C 3
B D 7
A E 9
A B 10

1.首先我们选择CD边,分别求得其对应的下标p1=3,p2=4;然后去求CD两颗树的父节点,由于其父节点是自身,我们返回的是m1=3,m2=4,m1不等于m2,说明两个节点不在同棵树上,可以作为最小生成树的一条边,加入数组mytree中,然后把C和D连在一起,更新vex [3]=4;代表C的父亲此时是D,D的父亲是自身;

2.我们选择BC,得到p1=2,p2=3,其父节点分别是m1=2,m2=4;加入mytree,连接BC,并让vex[2]=4;

3.选择BD,得到的父节点都是m1=m2=4,都是D,所以忽略本条边,继续下一次循环 ,一直到遍历完所有的边.

4.最后输出mytree中的数据,就是我们要求的最小生成树 .

你可能感兴趣的:(算法)