最小生成树问题-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; } }// 如果标记数组全部相等(成一颗树) 则循环结束 } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?