数据结构——克鲁斯卡尔(Kruskal)算法

article/2025/11/10 18:58:01

克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同,它的时间复杂度为O(eloge)(e为边数),适合于求边稀疏的网的最小生成树 。克鲁斯卡尔算法从另一途径求网的最小生成树。其基本思想是:假设连通网G,令最小生成树的初始状态为只有n个顶点而无边的非连通图T,概述图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。说白了,优先先选出全体边里最短的那几条,然后如果各分量还没连起来,就继续选择剩余没被选择的边里最短的,直到全部节点都连接在一起。
以下是数据结构中关于克鲁斯卡尔算法的操作(编程风格参考严蔚敏版数据结构)。

宏定义及头文件

#include<iostream>
#include<stdio.h>
using namespace std; 
typedef char VerTexType; 
typedef int ArcType; 
#define MaxInt 32767 
#define MVNum 100
#define ArcNum 100
#define OK 1
#define ERROR -1
int Vexset[MVNum];//辅助数组表示连通分量 
typedef int status;

图和边集合的声明

typedef struct{VerTexType vexs[MVNum] {'A','B','C','D','E','F'};ArcType arcs[MVNum][MVNum];int vexnum = 6,arcnum = 10;
}AMGraph; typedef struct{VerTexType Head;//起点 VerTexType Tail;//终点ArcType lowcast; 
}Edge[ArcNum];

说明:为了测试方便就提前写好了图里节点和边的数据。
edge是记录原始的图里全部边数据(包括起点、终点以及权值)

Kruskal辅助数组Vexset

int Vexset[MVNum];//辅助数组表示连通分量 

这里一定要理解好Vexset数组的作用和意义。如果这个理解不好,就像prim算法不理解close数组一样,核心算法就理解不了了。
Vexset是以下标i表示节点,以数组的值表示该点的连通量。
如:Vexset[1] = 0,Vexset[0] = 0.表示B节点和A节点是同一个连通量(B的下标为1,A的下标为0)。

Kruskal核心算法:

void Kruskal(AMGraph &G){Edge edge;InitailEdge(G,edge);sort(G,edge);
//	ShowEdge(G,edge);for(int i=0;i<G.vexnum;i++){Vexset[i] = i;//每个节点自成一个分量 }int headi,taili;//边起点的下标、边终点的下标 int headt,tailt;//操作连通分量时的中间量 for(int i=0;i<G.arcnum;i++){headi = LocateVex(G,edge[i].Head); taili = LocateVex(G,edge[i].Tail); headt = Vexset[headi];tailt = Vexset[taili];if(headt!=tailt){//如果两个点不是同一个连通分量cout<<edge[i].Head<<"-"<<edge[i].lowcast<<"-"<<edge[i].Tail<<endl;for(int j=0;j<G.vexnum;j++){if(Vexset[j]==headt){//合并选出来的两个节点,使其处于同一个连通分量Vexset[j] = tailt;}}}} 
}

代码执行过程:
1、初始化边集合:把图里全部边加入边集合(集合里边的数量就是图里边的数量)。
2、初始化辅助数组Vexset:每个节点自己属于一个连通量(毕竟刚开始谁都没连接谁是吧)。
3、将边集合按照边权值从小到大排序,让循环操作从小边到大边的顺序进行选择。
4、循环获取每一条边的起点和终点。
5、如果起点和终点不是同一个连通分量,就把两个点连起来(合并连通量)。
6、全部点同属一个连通分量,循环结束。

举例子手推算法过程:

此次选的例子还是书上的例子:
在这里插入图片描述
老样子先弄出邻接矩阵:
在这里插入图片描述

第一步:初始化边集合
在这里插入图片描述
第二步:初始化Vexset集合(每个节点分别代表一个连通分量),ABCDEF下标分别对应012345,所以ABCDEF的连通量分别是012345。
在这里插入图片描述
第三步(第二第三步可互换):边集合按边权值从小到大排序
在这里插入图片描述
第四步:大循环获取每一条边,看其两个端点是否是同一个连通量(因为边集合排过序了,每一次选择的肯定是未被选择的边集合里最短的边)。下面是大循环的执行过程:
1)、选出AC(权值为1),此时A和C的连通分量不一样(A是0,C是2),将AC的连通量合并(此处的操作是将A的连通量修改成和C一致),此时A和C连通量都是2;小循环未发现和C变化前连通量相同(0)的其它节点(这一步的目的是找出原来的起点,一起并入边集合中),故无实际操作;
此时的Vexset为{2,1,2,3,4,5}。
在这里插入图片描述
2)、选出DF(权值为2),此时D和F的连通分量不一样(D是3,F是5),将DF的连通量合并,此时D和F连通量都是5;小循环未发现和D变化前连通量相同(3)的其它节点,故无实际操作;
此时的Vexset为{2,1,2,5,4,5}。
在这里插入图片描述
3)、选出BE(权值为3),此时BE连通分量不一样(B是1,E是4),将BE连通量合并,此时B和E连通量都是4。小循环未发现和B变化前连通量相同(1)的其它节点,故无实际操作;
此时的Vexset为{2,4,2,5,4,5}。
在这里插入图片描述
4)、选出CF(权值为4),此时CF的连通量不一样(C是2,F是5),将CF连通量合并,此时C和F连通量都是5。小循环发现和C变化前连通量相同(2)的其它节点A,故将A的连通量也修改成F的连通量(5);
此时的Vexset为{5,4,5,5,4,5}。
在这里插入图片描述
5)、选出AD(权值为5),此时AD连通量一样(A是5,D是5),无操作。
6)、选出BC,此时BC连通分量不一样(B是4,C是5),将BC连通量合并,此时B和C连通量都是5。小循环发现和B变化前连通量相同(4)的其它节点E,故将E的连通量也修改成C的连通量(5);
此时的Vexset为{5,5,5,5,5,5}。
在这里插入图片描述
往后循环,每个点的连通分量都一致,故均无实际操作,直至循环结束。

运行结果

在这里插入图片描述

源代码

#include<iostream>
#include<stdio.h>
using namespace std; 
typedef char VerTexType; 
typedef int ArcType; 
#define MaxInt 32767 
#define MVNum 100
#define ArcNum 100
#define OK 1
#define ERROR -1
int Vexset[MVNum];//辅助数组表示连通分量 
typedef int status;
typedef struct{VerTexType vexs[MVNum] {'A','B','C','D','E','F'};ArcType arcs[MVNum][MVNum];int vexnum = 6,arcnum = 10;
}AMGraph; typedef struct{VerTexType Head;//起点 VerTexType Tail;//终点ArcType lowcast; 
}Edge[ArcNum];status CreateUDN(AMGraph &G){//创建无向图 	for(int i=0;i<G.vexnum;i++){for(int j=0;j<G.vexnum;j++){if(i==j){G.arcs[i][j] = 0;}elseG.arcs[i][j] = MaxInt;//初始状态全部节点之间相互不可达}}G.arcs[0][1]=6;G.arcs[0][2]=1;G.arcs[0][3]=5;G.arcs[1][2]=5;G.arcs[1][4]=3;G.arcs[2][3]=5;G.arcs[2][4]=6;G.arcs[2][5]=4;G.arcs[3][5]=2;G.arcs[4][5]=6;for(int i=0;i<G.vexnum;i++){for(int j=i+1;j<G.vexnum;j++){if(G.arcs[i][j]!=MaxInt){G.arcs[j][i] = G.arcs[i][j];} }}//矩阵对称 return OK; 
}void ShowGraph(AMGraph G){cout<<" ";for(int i=0;i<G.vexnum;i++){cout<<" "<<G.vexs[i];}cout<<endl;for(int i=0;i<G.vexnum;i++){cout<<G.vexs[i]<<" ";for(int j=0;j<G.vexnum;j++){if(G.arcs[i][j]==MaxInt){cout<<"* ";}else{cout<<G.arcs[i][j]<<" ";}}cout<<endl;}
}int LocateVex(AMGraph G, VerTexType v){int i;for(i=0;i<G.vexnum;i++){if(G.vexs[i]==v){return i;}} return ERROR;
}VerTexType Transform(AMGraph G, int vn){return G.vexs[vn]; 
}void InitailEdge(AMGraph G,Edge &edge){//初始化边表 int arcnum = 0;for(int i=0;i<G.vexnum;i++){//纵列为起点 for(int j=i+1;j<G.vexnum;j++){//横行为终点 if(G.arcs[i][j]!=MaxInt&&G.arcs[i][j]!=0){edge[arcnum].Head = Transform(G,i);edge[arcnum].Tail = Transform(G,j);edge[arcnum].lowcast = G.arcs[i][j];arcnum++;}}} 
}void sort(AMGraph G,Edge &edge){VerTexType tv;ArcType tl;for(int i=0;i<G.arcnum;i++){for(int j=0;j<G.arcnum-i-1;j++){if(edge[j].lowcast>edge[j+1].lowcast){//直接写对象互换报错,忘记咋互换对象了,这样写有点麻烦,先将就着用吧 ,这个操作不是重点 tv = edge[j].Head;edge[j].Head = edge[j+1].Head;edge[j+1].Head = tv;tv = edge[j].Tail;edge[j].Tail = edge[j+1].Tail;edge[j+1].Tail = tv;tl = edge[j].lowcast;edge[j].lowcast = edge[j+1].lowcast;edge[j+1].lowcast = tl;}}}
}void ShowEdge(AMGraph G,Edge edge){for(int i=0;i<G.arcnum;i++){cout<<edge[i].Head<<"-"<<edge[i].lowcast<<"-"<<edge[i].Tail<<endl;}
}void ShowVexset(AMGraph G){for(int i=0;i<G.vexnum;i++){cout<<Vexset[i]<<" ";} cout<<endl;
}void Kruskal(AMGraph &G){Edge edge;InitailEdge(G,edge); 
//	ShowEdge(G,edge);sort(G,edge);
//	ShowEdge(G,edge);for(int i=0;i<G.vexnum;i++){Vexset[i] = i;//每个节点自成一个分量 }int headi,taili;//边起点的下标、边终点的下标 int headt,tailt;//操作连通分量时的中间量 for(int i=0;i<G.arcnum;i++){headi = LocateVex(G,edge[i].Head); //起点下标 taili = LocateVex(G,edge[i].Tail); //终点下标 headt = Vexset[headi];//获取起点的连通分量 tailt = Vexset[taili];//获取终点的连通分量 if(headt!=tailt){//如果两个点不是同一个连通分量cout<<edge[i].Head<<"-"<<edge[i].lowcast<<"-"<<edge[i].Tail<<endl;for(int j=0;j<G.vexnum;j++){if(Vexset[j]==headt){//更新Vexset数组,把改起点的连通分量改成和终点连通分量一致(其实就是合并连通分量) Vexset[j] = tailt;
//					ShowVexset(G);}}}} 	
}int main(){AMGraph G;CreateUDN(G);ShowGraph(G);Kruskal(G); return 0;
} 

http://chatgpt.dhexx.cn/article/nJPRrbFP.shtml

相关文章

克鲁斯卡尔(Kruskal)算法(严蔚敏C语言)

克鲁斯卡尔算法(Kruskal) ​ 克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同&#xff0c;它的时间复杂度为O&#xff08;eloge&#xff09;&#xff08;e为网中的边数&#xff09;&#xff0c;所以&#xff0c;适合于求边稀疏的网的最小生成树 。 ——百…

4.《学会提问》

1.书中的观点 批判性思维会教你很多技能和态度&#xff0c;让你理性地找到对自己有意义的答案并为此感到自豪。批判性思维鼓励你倾听他人&#xff0c;向别人学习&#xff0c;同时掂量别人所说的话&#xff0c;看看它们的分量如何。如此&#xff0c;你将了解到我们必须要依赖他人…

如何提问——提问的艺术

转存失败重新上传取消https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md

《提问的艺术》读书笔记

分享读《提问的艺术》的xmind总结&#xff0c;每周听一本书&#xff0c;打卡第一周

(转载) 提问的艺术

虽然这是老话常谈&#xff0c;但是最近的回答问题的过程中&#xff0c;有点感触。你问题问的好&#xff0c;问的准确&#xff0c;回答你的人才有积极性给你答复&#xff0c;这样你又可以更快的解决你的问题。好多人不知道如何提问&#xff0c;所以我打算把这篇老文章转过来置顶…

论程序员提问的艺术

最近工作比较忙&#xff0c;加上空闲时间大部分都是在维护开发【云狗AI】&#xff0c;所以也有一段时间没更新视频了&#xff0c;有不懂的&#xff0c;也可以问一下【云狗AI】以后我也会花更多的时间在维护这个项目中。争取给大家带来更好的体验。 主要是因为最近没发现什么特…

提问的艺术[转]

这是刚看到的 所以转一下 也是警示自己的伸手行为 原文&#xff1a;How To Ask Questions The Smart Way 译文链接&#xff1a;提问的艺术 作者&#xff1a; 艾瑞克.史蒂文.雷蒙德(Eric Steven Raymond) <esrthyrsus.com> 瑞克.莫恩(Rick Moen) <respond-autolinuxmaf…

【提问的艺术】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

提问的艺术/怎么高效的提问

唉,可能是因为见过的人多了吧,不管是在现实世界中,还是在网络上,总是需要向别人询问一些东西的, 有的人来问,谦谦有礼,感觉很好,帮助了别人,也没损失什么,有的人来问,就是真心不想搭理他,搭理了他就是 浪费自己的时间,还心理不好受的. 今天不说现实中怎么提问.就说在互联网上.…

《提问的艺术》读后感

前言提问前 他明明能帮到我却不帮我提问前必知必会的一些事关于搜索引擎 提问时 找准对象学会停顿组织你的问题清晰的发问低声下气代替不了做自己的家庭作业删除无意义的要求不要把问题标记为紧急 即使对你而言的确如此礼貌总是有益的对待无礼提问禁区 总结 前言 众所周知&am…

提问的艺术,原文链接

你提的问题没有任何有用的信息&#xff0c;u hava a poop。 提问的艺术 今天在拉屎的时候看到QQ群里有人问问题&#xff0c;那个问题太他妈蠢了&#xff0c;所以我就写了这一片博客&#xff0c;如果再有人问愚蠢的问题我就把这篇博客丢给他。 提问的艺术 原文链接&#xff…

《提问的艺术:如何快速获得答案》(精读版)

《提问的艺术》《How-To-Ask-Questions-The-Smart-Way》 https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way © 2001 Eric Steve Raymond © 2001 D.H.Grand(nOBODY/Ginux) © 2012 Conmajia《提问的艺术&#xff1a;如何快速获得答案》原著Eric…

Serverlet理解

部分转载自&#xff1a;https://blog.csdn.net/javaloveiphone/article/details/8154791 从上图可以看出 Tomcat 的容器分为四个等级&#xff0c;真正管理Servlet 的容器是Context 容器&#xff0c;一个 Context 对应一个 Web 工程。除了将 Servlet 包装成 StandardWrapper 并作…

java serverlet_Serverlet程序

Serverlet是用Java编写的服务器端程序;主要用于交互地浏览和修改数据&#xff0c;生成动态Web内容; 一个serverlet就是一个继承于HttpServlet抽象类的Java类&#xff1b;下面先看一个简单的例子 import javax.servlet.*;importjavax.servlet.http.HttpServlet;importjavax.serv…

serverlet 区别_jsp serverlet 区别

JSP和Servlet的概念对于JSP初学者来说比较不清楚&#xff0c;以下总结一些个人看法&#xff1a; (1).简单的来说Jsp就是含有Java代码的html&#xff0c;而servlet是含有html的Java代码&#xff1b; (2).Jsp最终也是被解释为servlet并编译再执行&#xff0c;Jsp不过是servlet的另…

Java开发之ServLet详解

一、什么是ServLet&#xff1f; serverLet是javaEE中运行于服务器端的&#xff0c;用于接收和响应HTTP协议的请求的程序。 二、ServLet的三种实现方式 1、实现ServLet接口 步骤&#xff1a; &#xff08;1&#xff09;实现ServLet接口 &#xff08;2&#xff09;重写包括s…

Serverlet的生命周期

1.Servlet的生命周期 Servlet没有main()方法&#xff0c;不能独立运行&#xff0c;它的运行完全由Servlet引擎来控制和调度。所谓生命周期 &#xff0c;指的是servlet容器何时创建servlet实例、何时调用其方法进行请求的处理、何时并销毁其实例的 整个过程。其完整的周期包括…

VRRP协议以及关联Track详解

一、实验 详细解释可以直接查看下面连接&#xff0c;是我转发CSDN大佬的连接&#xff0c;直接上实验 VRRP详解 R1&#xff0c;R2充当两个网段的PC、分别是13.1.1.0 和24.2.2.0 R5&#xff0c;R6充当两个PC的接入层交换机 R5 eth0/1属于vlan 2000&#xff0c;R6 eth0/2属于vlan…

理解VRRP协议

VRRP即虚拟路由冗余协议(Virtual Router Redundancy Protocol)&#xff0c;它是为了避免路由器出现单点故障的一种容错协议。 如图1所示&#xff0c;我们把多个运行着VRRP协议的路由器抽象成一个虚拟路由器&#xff08;从外边来看&#xff0c;就像只有一个真实的路由器在工作&…

VRRP协议原理

目录 1、VRRP概述 2、VRRP概念 3、VRRP报文 4、VRRP工作原理 5、VRRP状态机 1、VRRP概述 在基于TCP/IP协议的网络中&#xff0c;为了保证不直接物理连接的设备之间的通信&#xff0c;必须指定路由。目前常用的指定路由的方法有两种&#xff1a;一种是通过路由协议&#xff08;比…