最小生成树(C语言简单实现)

article/2025/9/16 10:38:44

最小生成树

本文参考自《大话数据结构》

一个连通图的生成树是一个极小的连通子图,它含有图中全部顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树

找连通网的最小生成树,经典的有两种算法:普里姆算法和克鲁斯卡尔算法。

普里姆(Prim)算法

/* Prim算法生成最小生成树 */
void MiniSpanTree_Prim(MGraph G){int min, i, j, k;int adjvex[MAXVEX];	//保存相关顶点下标int lowcost[MAXVEX];	//保存相关顶点间边的权值lowcast[0] = 0;	//初始化第一个权值为0,即v0加入生成树,lowcost的值为0,在这里就是此下标的顶点已经加入生成树adjvex[0] = 0;	//初始化第一个顶点下标为0for(i = 1;i<G.numVertexes;i++){	//循环除下标为0外的全部顶点lowcost[i] = G.arc[0][i];		//将v0顶点与之有边的权值存入数组adjvex[i] = 0;	//初始化都为v0的下标}for(i=1;i<G.numVertexes;i++){min = INFINITY;	//初始化最小权值为无穷大,通常设置为很大的数字j = 1;k = 0;while(j<G.numVertexes){		//循环全部顶点if(lowcost[j] != 0 && lowcost[j] < min){	//如果权值不为0且权值小于minmin = lowcost[j];		//让当前权值成为最小值k = j;		//将当前最小值的下标存入k}j++;}printf("(%d,%d)",adjvex[k],k);	//打印当前顶点边中权值最小边lowcost[k] = 0;	//将当前顶点的权值设置为0,表示此顶点已经完成任务for(j=1;j<G.numVertexes;j++){	//循环所有顶点if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j]){	//若下标为k顶点各边权值小于此前这些顶点未被加入生成树权值lowcost[j] = G.arc[k][j];		//将较小权值存入lowcostadjvex[j] = k;	//将下标为k的顶点存入adjvex}}}
}

假设N=(P,{E})是连通网,TE是N上最小生成树中边的集合。算法从U={u0}(u0∈V),TE={}开始。重复执行下述操作:在所有u∈Uv∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入集合TE,同时v0并入U,直至U=V为止。此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。

由算法代码中的循环嵌套可得此算法复杂度为O(n^2)

克鲁斯卡尔(Kruskal)算法

普里姆算法是以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树的。
而克鲁斯卡尔算法是以边为目标进行构建,因为权值是在边上,直接去找最小值的边来构建生成树也是很自然的想法,只不过构建时要考虑是否会形成环路而已。

edge边集数组结构的定义代码:

/* 对边集数组Edge结构的定义 */
typedef struct{int begin;int end;int weight;
}Edge;

克鲁斯卡尔算法代码如下:

/* kruskal算法生成最小生成树 */
void MiniSpanTree(MGraph G){	//生成最小生成树int i, n, m;Edge edges[MAXEDGE];	//定义边集数组int parent[MAXVEX];	//定义一数组用来判断边与边是否形成环路/* 此处省略将邻接矩阵G转化为边集数组edges并按权由小到大排序的代码 */for(i = 0;i<G.numVertexes;i++)parent[i] = 0;	//初始化数组值为0for(i = 0;i<G.numEdges;i++){	//循环每一条边n = Find(parent, edges[i].begin);m = Find(parent, edges[i].end);if(n != m){		//假如n与m不等,说明此边没有与现有生成树形成环路parent[n] = m;	//将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合中printf("(%d,%d) %d", edges[i].begin, edges[i].end, edges[i].weight);}}
}int Find(int* parent, int f){	//查找连线顶点的尾部下标while(parent[f] > 0)f = parent[f];return f;
}

克鲁斯卡尔算法实现的定义:

  1. 假设N={V,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连同分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。以此类推,直到T中所有顶点都在同一连通分量上为止。
  2. 此算法的Find函数由边数e决定,时间复杂度为O(loge),而外面有一个for循环e次,所以克鲁斯卡尔算法的时间复杂度为O(eloge)
  3. 对比两个算法,克鲁斯卡尔算法主要是针对边展开,边数少的时候效率会非常高,所以对于稀疏图有很大的优势;而普里姆算法对于边数非常多的稠密图情况会更好一些。

例子

最小生成树例子图解

克鲁斯卡尔

#include <stdio.h>typedef char VertexType;
typedef int EdgeType;struct Edge{int begin;int end;EdgeType weight;
};/* 插入排序 */
void sort(Edge* edge, int maxedge){int i, j, min;Edge temp;for(i=0;i<maxedge-1;i++){min = i;for(j=i+1;j<maxedge;j++){if(edge[min].weight > edge[j].weight){min = j;}}if(min != i){temp.begin = edge[i].begin;temp.end = edge[i].end;temp.weight = edge[i].weight;edge[i].begin = edge[min].begin;edge[i].end = edge[min].end;edge[i].weight = edge[min].weight;edge[min].begin = temp.begin;edge[min].end = temp.end;edge[min].weight = temp.weight;}}
}int Find(int* parent, int i){while(parent[i] > 0)i = parent[i];return i;
}/* kruskal算法生成最小生成树 */
void MiniSpanTree(int maxvex, int maxedge){int i, n, m;Edge edge[maxedge];	//定义边集数组 int parent[maxvex];	//定义一数组判断是否形成回路//===============================================此处省略了构建图的过程,图的构建可以看看我其他博客 printf("输入边和权值:\n");for(i=0;i<maxedge;i++)scanf("%d,%d,%d",&edge[i].begin, &edge[i].end, &edge[i].weight);//===============================================sort(edge, maxedge);for(i=0;i<maxvex;i++)parent[i] = 0;	//初始化for(i=0;i<maxedge;i++){n = Find(parent, edge[i].begin);m = Find(parent, edge[i].end);if(n != m){	//不等,说明没有生成环形树 parent[n] = m;	//将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合中printf("(%d,%d)%d\n",edge[i].begin, edge[i].end, edge[i].weight); }}
}int main(){int maxvex;int maxedge;printf("输入顶点数和边数:\n");scanf("%d,%d",&maxvex, &maxedge);MiniSpanTree(maxvex,maxedge);return 0;
}

最小生成树-克鲁斯卡尔

普里姆

#include <stdio.h>
#include <stdlib.h>typedef char VertexType;	//顶点类型
typedef int EdgeType;	//边上的权值类型
#define MAXVEX 100	//最大顶点数 
#define INFINITY 65535	//代表无穷大
bool visited[MAXVEX];	//标记结点是否遍历过 struct MGraph{VertexType vexs[MAXVEX];	//顶点表EdgeType arc[MAXVEX][MAXVEX];	//邻接矩阵,边表int numVertexes, numEdges;	//图中顶点数和边数 
};/* 建立无向图的邻接矩阵表示 */
void CreateMGraph(MGraph* G){int i, j, k, w;printf("输入顶点数和边数:\n");scanf("%d,%d",&G->numVertexes, &G->numEdges);printf("输入顶点值(字符):\n");for(i=0;i<G->numVertexes;i++){//读取顶点信息,建立顶点表 getchar();	//吃掉回车 scanf("%c", &G->vexs[i]);}for(i=0;i<G->numVertexes;i++)for(j=0;j<G->numVertexes;j++)G->arc[i][j] = INFINITY;	//邻接矩阵初始化for(k=0;k<G->numEdges;k++){	//读取numEdges条边,建立邻接矩阵 printf("输入边(vi,vj)上的下标i,下标j和权w:\n");scanf("%d,%d,%d",&i,&j,&w);G->arc[i][j] = w;G->arc[j][i] = G->arc[i][j];	//无向图的邻接矩阵关于主对角线对称 }
}/* Prim算法生成最小生成树 */
void MiniSpanTree_Prim(MGraph* G){int min, i, j, k;int adjvex[MAXVEX];	//保存相关顶点下标int lowcost[MAXVEX];	//保存相关顶点间边的权值lowcost[0] = 0;	//初始化第一个权值为0,即v0加入生成树,lowcost的值为0,在这里就是此下标的顶点已经加入生成树adjvex[0] = 0;	//初始化第一个顶点下标为0for(i = 1;i<G->numVertexes;i++){	//循环除下标为0外的全部顶点lowcost[i] = G->arc[0][i];		//将v0顶点与之有边的权值存入数组adjvex[i] = 0;	//初始化都为v0的下标}for(i=1;i<G->numVertexes;i++){min = INFINITY;	//初始化最小权值为无穷大,通常设置为很大的数字j = 1;k = 0;while(j<G->numVertexes){		//循环全部顶点if(lowcost[j] != 0 && lowcost[j] < min){	//如果权值不为0且权值小于minmin = lowcost[j];		//让当前权值成为最小值k = j;		//将当前最小值的下标存入k}j++;}printf("(%d,%d)%d\n",adjvex[k],k,lowcost[k]);	//打印当前顶点边中权值最小边lowcost[k] = 0;	//将当前顶点的权值设置为0,表示此顶点已经完成任务for(j=1;j<G->numVertexes;j++){	//循环所有顶点if(lowcost[j] != 0 && G->arc[k][j] < lowcost[j]){	//若下标为k顶点各边权值小于此前这些顶点未被加入生成树权值lowcost[j] = G->arc[k][j];		//将较小权值存入lowcostadjvex[j] = k;	//将下标为k的顶点存入adjvex}}}
}int main(){MGraph* G = (MGraph*)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(G);return 0;
}

最小生成树-普里姆


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

相关文章

最小生成树怎么画?

介绍最小生成树的图形画法~以下图为例&#xff1a; 1、从1开始&#xff0c;以1为顶点画圈。在红色线经过的部分中&#xff0c;可见权重分别为6、1、5&#xff0c;最小权重为1。如下图。 2、以上图中得到的1、3为顶点的图中&#xff0c;继续画线&#xff0c;图为黄色线部分。经过…

最小生成树算法

最小生成树算法 生成树的概念最小生成树算法Prim算法Kruskal算法 生成树的概念 若图是连通的无向图或强连通的有向图&#xff0c;则从其中任一顶点出发&#xff0c;调用一次 d f s dfs dfs或者 b f s bfs bfs后&#xff0c;可以系统的访问图中所有顶点。若图是有根的有向图 &a…

【图】最小生成树

最小生成树&#xff1a;构造连通网的最小代价生成树。 最小生成树有两种算法&#xff1a;普利姆算法、克鲁斯卡尔算法。 普利姆&#xff08;Prim&#xff09;算法 加点&#xff0c;选择相邻点中边权最小的 需要两个一维数组&#xff0c;一个存权值&#xff0c;另一个存起始点…

最小生成树-Kruskal算法

数据结构:最小生成树-Kruskal算法 什么是最小生成树&#xff0c;最小生成树就是求图中把所有点都访问到的最短路径的长度 Kruskal算法采用的是边贪心思想&#xff0c;我们先大概讲一下它的大概思想&#xff0c;首先我们先假设先隐藏所有的边&#xff0c;这样每个点会成为一个连…

最小生成树(kruskal算法)

一、概述 最小生成树问题顾名思义&#xff0c;概括来说就是路修的最短。 接下来引入几个一看就明白的定义&#xff1a; 最小生成树相关概念&#xff1a; 带权图&#xff1a;边赋以权值的图称为网或带权图&#xff0c;带权图的生成树也是带权的&#xff0c;生成树T各边的权值…

最小生成树、最短路径树

一、最小生成树与最短路径树的区别 最小生成树能够保证整个拓扑图的所有路径之和最小&#xff0c;但不能保证任意两点之间是最短路径。 应用如网络部线&#xff0c;把所有的电脑(服务器&#xff1f;&#xff09;都连起来用的网线(光纤&#xff1f;&#xff09;最少&#xff0c…

【图解算法】最小生成树

今天我们介绍图论中的另一部分&#xff0c;最小生成树。 对图的最短路感兴趣的同学可以看&#xff1a; 【图解算法】一次解决最短路径问题 目录 1. 最小生成树简介2. Prim算法2.1 模板题2.2 思路模板2.3 代码实现2.3 prim 算法的优化 3. Kruskal算法3.1 模板题3.2 思路模板3.3…

数据结构—最小生成树

目录 一、生成树 二、最小生成树&#xff08;代价最小树&#xff09; 三、求最小生成树 1、Prim算法&#xff08;普里姆&#xff09; 2.Kruskal 算法&#xff08;克鲁斯卡尔&#xff09; 3.Prim算法和Kruskal算法对比 一、生成树 连通图的生成树是包含图中全部顶点的一个…

最小生成树详解

目录 最小生成树简介 什么是树 什么是生成树 什么是最小生成树 最小生成树的做法 Kruscal&#xff08;克鲁斯卡尔&#xff09;算法 思路 代码 其他算法 最小生成树简介 什么是树 树&#xff08;tree&#xff09;是一种特殊的图&#xff0c;一个图要成为树要满足三个条…

最小生成树的实现(C语言)

今天做洛谷的时候刷到好多图论的题&#xff0c;发现自己在这一方面算法的掌握还是有待提高啊。在这就先介绍最小生成树的算法吧。 最小生成树 最小生成树&#xff08;minimum spanning tree&#xff09;是由n个顶点&#xff0c;n-1条边&#xff0c;将一个连通图连接起来&…

详解: 最小生成树

详解: 最小生成树算法 最小生成树&#xff08;Minimum Spanning Tree, MST&#xff09;是在一个给定的无向图G(V, E)中求一棵树&#xff0c;使得这棵树有图G的所有顶点&#xff0c;且所有边都来自图G中的边&#xff0c;并且满足整棵树的边权之和最小. 最下生成树满足如下性质…

最小生成树

目录 一、最小生成树定义&#xff1a; 二、普里姆算法&#xff08;Prim&#xff09; 三、Kruskal算法&#xff08;克鲁斯卡尔&#xff09; 小结&#xff1a; 下一篇&#xff1a;最短路径算法 问题&#xff1a; 一、最小生成树定义&#xff1a; 对于一个带权连通无向图G(V,E)&am…

最小生成树——Prim算法(详细图解)

目录 最小生成树的概念 经典题目 prim算法简介 prim算法解析 &#xff08;详细图解&#xff09; 代码实现 代码实战 最小生成树的概念 在一给定的无向图G (V, E) 中&#xff0c;(u, v) 代表连接顶点 u 与顶点 v 的边&#xff0c;而 w(u, v) 代表此的边权重&#xff0c;若…

最小生成树入门(图解)

1.什么是最小生成树 来看百度百科的定义 一个有 n 个结点的连通图的生成树是原图的极小连通子图&#xff0c;且包含原图中的所有 n个结点&#xff0c;并且有保持图连通的最少的边。最小生成树可以用kruskal&#xff08;克鲁斯卡尔&#xff09;算法或prim&#xff08;普里姆&am…

最小生成树(Prim、Kruskal)算法,秒懂!

前言 在数据结构与算法的图论中&#xff0c;(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法。但是可能很多人对概念不是很清楚&#xff0c;什么是最小生成树? 一个有 n 个结点的连通图的生成树是原图的极小连通子图&#xff0c;且包含原图中的所有 n 个结点&…

最小生成树详解(模板 + 例题)

作为一个伪ACMer,先来首广为人知的打油诗: 模拟只会猜题意&#xff0c;贪心只能过样例&#xff0c;数学上来先打表&#xff0c;规律一般是DP&#xff0c;组合数学碰运气&#xff0c;计算几何瞎暴力&#xff0c;图论一顿套模板&#xff0c;数论只会GCD,递归递推伤不起&#xff0…

Linux开启/关闭防火墙

一、查看防火墙状态&#xff1a; systemctl status firewalldinactive表示防火墙为关闭状态。 二、开启防火墙&#xff1a; systemctl start firewalld启动后无任何提示&#xff0c;再次查看防火墙状态&#xff0c;可以看到变成active&#xff0c;成功启动。 三、关闭防火墙…

linux服务器查看防火墙是否关闭,linux查看防火墙是否关闭了的方法

linux查看防火墙是否关闭了的方法 发布时间&#xff1a;2020-04-02 10:49:28 来源&#xff1a;亿速云 阅读&#xff1a;62 作者&#xff1a;小新 今天小编给大家分享的是linux查看防火墙是否关闭了的方法&#xff0c;很多人都不太了解&#xff0c;今天小编为了让大家更加了解li…

linux操作系统关闭防火墙,linux操作系统关闭防火墙的方法

防火墙是一项协助确保信息安全的设备,会依照特定的规则,允许或是限制传输的数据通过。简单的来说防火墙的作用就是保护你的网络免受非法用户的侵入,虽然防火墙是为了你网络安全而存在,但是同时也限制了你上网操作,有很多人会选择关闭防火墙,windows操作系统的防火墙好关闭…

linux为什么要关闭防火墙,Linux怎么样关闭防火墙

Linux系统下面自带了防火墙iptables&#xff0c;iptables可以设置很多安全规则。但是如果配置错误很容易导致各种网络问题&#xff0c;那么如果要关闭禁用防火墙怎么操作呢?下面就让学习啦小编给大家说说Linux怎么关闭防火墙吧。 Linux关闭防火墙的方法 如果启动的iptables防火…