邻接表的最小生成树kruskal算法

1 篇文章 0 订阅
1 篇文章 0 订阅

###邻接表
####无向网节点定义

typedef struct ANode{
    int adjvex;
    struct ANode * nextarc;
    int info;                      
}ArcNode;
//边节点类型

typedef struct Vnode{
    char data;
    ArcNode *firstarc;
}VNode,AdjList[MAXV];
//表头结点信息

typedef struct {
    AdjList adjlist;                       
    int n,e;                                //顶点数,边数
}ALGraph;
//完整的图邻接表类型

-节点的逻辑结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hvl0kcFt-1610420600360)(https://thumbnail0.baidupcs.com/thumbnail/a34b63534a4c5731fc375a2ced9c2606?fid=3712045569-250528-914935062880222&time=1512262800&rt=sh&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-g3AcZxQysMKSclDCMnkRGanqcyc=&expires=8h&chkv=0&chkbd=0&chkpc=&dp-logid=7802409711651854039&dp-callid=0&size=c710_u400&quality=100&vuk=-&ft=video)]


###最小生成树Kruskal算法

  • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。

  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树
    引用勿在浮沙筑高台的图片

  • 生成过程:
    引用勿在浮沙筑高台的图片

###在计算机储存结构中

  • 邻接表G
    在这里插入图片描述

  • 最小生成树E的邻接表在这里插入图片描述

####NULL表示指针为空,null表示权值为空(避免重复权值)


###代码解读
-创建连通网G

void CreateALGraph(ALGraph *G){
    int i,j,k,w;
    ArcNode *p;
    printf("请输入顶点数和边数\n");
    scanf("%d,%d",&G->n,&G->e);
    for (i=0; i < G->n;i++) {
        printf("请输入顶点信息\n");
        scanf("%s",&G->adjlist[i].data);
        G->adjlist[i].firstarc=NULL;
    }
    
    for (k=0; k<G->e; k++) {
        printf("输入第%d条边的两顶点编号和权值:",k+1);
        scanf("%d,%d,%d",&i,&j,&w);             //顶点编号0,顶点编号1,两者的权值
        
        p=(ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex=j;
        p->info = w;
        p->nextarc=G->adjlist[i].firstarc;
        G->adjlist[i].firstarc=p;
        
        p=(ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex=i;
        p->info =NULL;
        p->nextarc=G->adjlist[j].firstarc;
        G->adjlist[j].firstarc=p;
    }
}


-最小生成树E

void CreateKruskal(ALGraph *G,ALGraph *E){
    int i ,j=0;
    int bj[MAXV];
    int a[MAXV],b[MAXV]; //a里面放权值,b里放相应权值对应的顶点行(减少遍历次数,提高效率)
    int min,min_dex,ls;//连锁标记
    int result=0;
    E->n=G->n;
    E->e=G->e;
    ArcNode *p;
    ArcNode *q;
    for (i=0; i<G->n; i++) {   //循环顶点 a,b,c,d,e,f
        for (p=G->adjlist[i].firstarc; p!=NULL; p=p->nextarc){
            if (p->info!=NULL) {
                a[j]=p->info;
                b[j]=i;
                j++;
            }
            
        }
    }                       //a,b 创建好,j中是权值个数
    for (i=0; i<G->n; i++) {
        bj[i]=i;
    }//建立bj[]标记表(不同标记,代表不同的树)
    while(result==0){
        min=a[0];
        min_dex=0;
        for (i=1; i<j; i++) {
            if (a[i]< min) {
                min=a[i];
                min_dex=i;
            }
        }
        a[min_dex]=a[j-1]+1;  //去掉最小值(把当前拿出的最小值变成 最大值+1)
        for (p=G->adjlist[b[min_dex]].firstarc; p!=NULL; p=p->nextarc){
            if (p->info==min) {
                if (bj[b[min_dex]] != bj[p->adjvex]) {
                    ls=bj[p->adjvex];
                    for (i=0; i<G->n; i++) {  //这个循环解决当标记表为{0,0,1,1}时,连接一个0和1,自动把剩下的1变为0,【达到连接两个树的目的】 
                        if (bj[i]==ls) {
                            bj[i]=bj[b[min_dex]];
                        }
                    }
                    q=(ArcNode *)malloc(sizeof(ArcNode));
                    q->adjvex=p->adjvex;
                    q->info=p->info;
                    q->nextarc=E->adjlist[b[min_dex]].firstarc;
                    E->adjlist[b[min_dex]].firstarc = q;
                }
            }//在a中找最小值,通过下标,在b中找哪一行顶点,判断p->info是否与最小值相等,相等就把adjvex 顶点信息  判断加入到E中
        result=1;
        for(i = 0; i<G->n; i++){
            if(a[0] != a[i])
            {
                result=0;
                break;
            }
        }//   如果标记数组全部相等(成一颗树) 则循环结束
    }
}

  • 本算法通过把邻接表G中每条边权值 读入a[]数组中,(通过b[]数组记录权值读入时的顶点行,提高搜索效率)。
  • 然后从a中拿最小权值,在G指定顶点行中查找,如果找到边节点,则判断该 边节点顶点 与 头结点顶点 是否属于同一颗树(看BJ[] 标记表数是否一样)
  • 如果不一样就加入到E邻接表中,并将等于 边节点标记数 全部改为 头结点标记数
  • 如果一样就不加入到E表中
  • 最后判断标记表是否是一棵树(标记表全相等)
  • 如果不等,进入下一次循环,又在a[]数组中找最小值拿入
  • 如果相等,结束循环

a[]={1,2,3,4,5,5,5,6,6,6}
bj[]={ 0, 1, 2, 3, 4, 5 } 相当于5颗树
对应 =A·B··C··D··E···F

第一次循环从a中拿出权值1,并在顶点A,C中找到,加入E后,bj[]={0,1,0,3,4,5}
第二次循环从a中拿出权值2,并在顶点D,F中找到,加入E后,bj[]={0,1,0,3,4,3}
第三次循环从a中拿出权值3,并在顶点B,E中找到,加入E后,bj[]={0,1,0,3,1,3}
第四次循环从a中拿出权值4,并在顶点C,F中找到,加入E后,bj[]={0,1,0,0,1,0}
第五次循环从a中拿出权值5,当在顶点A,D中找到时,因为A,D的标记均为0,表示在一颗树上,所以不放入E
第六次循环从a中拿出权值5,当在顶点B,C中找到时,因为B,C标记为1,0 不在同一颗树上,所以放入E,此时bj[]={0,0,0,0,0}
循环结束,打印结果


打印代码与main函数如下

void DispALGraph(ALGraph *G){
    int i,max=0,max_index=0;
    ArcNode *p;
    printf("图的邻接表如下:\n");
    printf("编号     顶点   相邻编号,权值\n");
    for (i=0; i<G->n; i++) {
        int j=0;
        printf("%4d %8c",i,G->adjlist[i].data);
        for (p=G->adjlist[i].firstarc; p!=NULL; p=p->nextarc) {
            printf(" --> ");
            printf("%d,%d",p->adjvex,p->info);
            j++;
            if (max<j) {
                max=j;
                max_index=i;
            }
        }
        printf("\t该顶点有%d个边\n",j);
    }
    printf("    编号%d有最多度\n",max_index);
    
}


int main(){
    ALGraph G;          //无向网连通网G
    ALGraph E;          //最小生成树
    CreateALGraph(&G);  //在每个头结点后面加上边的信息
    DispALGraph(&G);    //打印无向网
    CreateKruskal(&G,&E);//创建最小生成树
    DispALGraph(&E);//打印最小生成树
    return 0;
}

输入输出结果如下
在这里插入图片描述


其中两个生成树图来自

http://blog.csdn.net/luoshixian099/article/details/51908175

  • 8
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值