最小生成树问题-kruskal算法

kruskal适合稀疏图

具体步骤和演示看这个:https://www.cnblogs.com/ssyfj/p/9488850.html

复制代码
知识点:
1:步骤:
1)将图中的所有边都去掉。
2)将边按权值从小到大的顺序添加到图中,保证添加的过程中不会形成环 3)重复上一步直到连接所有顶点,此时就生成了最小生成树。这是一种贪心策略。
2:判断某条边<u, v>的加入是否会在已经选定的边集集合中形成环。
使用并查集,分别找出两个顶点u, v所在树的根节点。若根节点相同,说明u, v在同一棵树中,则u, v连接起来会形成环;
若根节点不同,则u, v不在一棵树中,连接起来不会形成环,而是将两棵树合并。
复制代码

(一)邻接矩阵结构

定义边结构体

typedef struct
{
    int begin;
    int end;
    int weight;
}Edge;

算法实现代码

复制代码
//邻接矩阵转边集数组
void MGraph2EdgeArr(MGraph G, Edge* edge);
//找到顶点index的根节点下标返回
int Find(int* parent, int index);
//使用克鲁斯卡尔算法进行最小生成树的创建
void MiniSpanTree_Kruskal(MGraph G);
//邻接矩阵转编辑数组,按照权值排序,由小到大
void MGraph2EdgeArr(MGraph G, Edge* edge)
{
    int i, j,k=0;
    Edge temp;
    int min;
    for (i = 0; i < G.numVertexes;i++)
    {
        for (j = i + 1; j < G.numVertexes;j++)
        {
            if (G.arc[i][j]!=INFINITY)    //有边
            {
                edge[k].begin = i;
                edge[k].end = j;
                edge[k].weight = G.arc[i][j];
                k++;
            }
        }
    }

    //按照冒泡大小进行排序
    for (i = 0; i < k;i++)
    {
        for (j = i; j < k;j++)
        {
            if (edge[j].weight<edge[i].weight)
            {
                temp = edge[i];
                edge[i] = edge[j];
                edge[j] = temp;
            }
        }
    }
}
int Find(int* parent, int f)
{
    while (parent[f] > 0)
        f = parent[f];
    return f;
}
void MiniSpanTree_Kruskal(MGraph G)
{
    Edge edges[MAXVEX];    //定义边集数组
    int parent[MAXVEX];    //定义生成树的父节点,也可以使用结构体,但是更加浪费空间
    int i,n,m;
    MGraph2EdgeArr(G, edges);    //邻接矩阵转边集数组

    //开始进行初始化
    for (i = 0; i < MAXVEX; i++)
        parent[i] = 0;    //这里是0代表根节点,我们也可以使用-1,正负无穷等
    //进行合并操作
    for (i = 0; i < G.numEdges;i++)
    {
        n = Find(parent, edges[i].begin);    //找到顶点edges[i].begin的根节点下标
        m = Find(parent, edges[i].end);        //找到顶点edges[i].end的根节点位置
        if (n!=m)    //若是根节点下标不是一样的,就说不在一棵树上,不会形成环,我们放心合并
        {
            parent[n] = m;    //将n树合并到m树,表示该边被放入生成树
        }
    }
}
复制代码

 (二)邻接表结构

复制代码
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;
//完整的图邻接表类型
复制代码
复制代码
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;
    }
}
复制代码
复制代码
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;
            }
        }//   如果标记数组全部相等(成一颗树) 则循环结束
    }
}
复制代码

 

posted on   Y-flower  阅读(65)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示