最小生成树构造算法--Prim算法,Kruskal算法(C语言)

最小生成树

最小生成树(minimum spanning tree)是由n个顶点,n-1条边,将一个连通图连接起来,且使权值最小的结构。
最小生成树可以用Prim(普里姆)算法或kruskal(克鲁斯卡尔)算法求出。

我们将以下面的带权连通图为例讲解这两种算法的实现:
带权连通图

注:由于测试输入数据较多,程序可以采用文件输入
这里写图片描述



Prim(普里姆)算法
时间复杂度:O(N^2)(N为顶点数)
prim算法又称“加点法”,用于边数较多的带权无向连通图
方法:每次找与之连线权值最小的顶点,将该点加入最小生成树集合中
注意:相同权值任选其中一个即可,但是不允许出现闭合回路的情况。
1
2
3
4
5
6
7

代码部分通过以下步骤可以得到最小生成树:

1.初始化:
lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0表示i点加入了MST。
mst[i]:表示对应lowcost[i]的起点,当mst[i]=0表示起点i加入MST。
由于我们规定最开始的顶点是1,所以lowcost[1]=0,MST[1]=0。即只需要对2~n进行初始化即可。

#define MAX 100  
#define MAXCOST 0x7fffffff  int graph[MAX][MAX];  void prim(int graph[][MAX], int n)  
{  int lowcost[MAX];  int mst[MAX];  int i, j, min, minid, sum = 0;  for (i = 2; i <= n; i++)  {  lowcost[i] = graph[1][i];//lowcost存放顶点1可达点的路径长度 mst[i] = 1;//初始化以1位起始点 }  mst[1] = 0;  

2.查找最小权值及路径更新
定义一个最小权值min和一个最小顶点ID minid,通过循环查找出min和minid,另外由于规定了某一顶点如果被连入,则lowcost[i]=0,所以不需要担心重复点问题。所以找出的终点minid在MST[i]中可以找到对应起点,min为权值,直接输出即可。
我们连入了一个新的顶点,自然需要对这一点可达的路径及权值进行更新,所以循环中还应该包括路径更新的代码。

for (i = 2; i <= n; i++)  {  min = MAXCOST;  minid = 0;  for (j = 2; j <= n; j++)  {  if (lowcost[j] < min && lowcost[j] != 0)  {  min = lowcost[j];//找出权值最短的路径长度 minid = j; //找出最小的ID }  }  printf("V%d-V%d=%d\n",mst[minid],minid,min); sum += min;//求和 lowcost[minid] = 0;//该处最短路径置为0 for (j = 2; j <= n; j++){  if (graph[minid][j] < lowcost[j])//对这一点直达的顶点进行路径更新 {  lowcost[j] = graph[minid][j];  mst[j] = minid;}  }  }  printf("最小权值之和=%d\n",sum);
}  

具体代码如下:

#include<stdio.h>    
#define MAX 100  
#define MAXCOST 0x7fffffff  int graph[MAX][MAX];  void prim(int graph[][MAX], int n)  
{  int lowcost[MAX];  int mst[MAX];  int i, j, min, minid, sum = 0;  for (i = 2; i <= n; i++)  {  lowcost[i] = graph[1][i];//lowcost存放顶点1可达点的路径长度 mst[i] = 1;//初始化以1位起始点 }  mst[1] = 0;  for (i = 2; i <= n; i++)  {  min = MAXCOST;  minid = 0;  for (j = 2; j <= n; j++)  {  if (lowcost[j] < min && lowcost[j] != 0)  {  min = lowcost[j];//找出权值最短的路径长度 minid = j; //找出最小的ID }  }  printf("V%d-V%d=%d\n",mst[minid],minid,min); sum += min;//求和 lowcost[minid] = 0;//该处最短路径置为0 for (j = 2; j <= n; j++){  if (graph[minid][j] < lowcost[j])//对这一点直达的顶点进行路径更新 {  lowcost[j] = graph[minid][j];  mst[j] = minid;}  }  }  printf("最小权值之和=%d\n",sum);
}  
int main()  
{  int i, j, k, m, n;  int x, y, cost;  //freopen("1.txt","r",stdin);//文件输入 scanf("%d%d",&m,&n);//m=顶点的个数,n=边的个数  for (i = 1; i <= m; i++)//初始化图 {  for (j = 1; j <= m; j++)  {  graph[i][j] = MAXCOST;  }  }   for (k = 1; k <= n; k++)  {  scanf("%d%d%d",&i,&j,&cost);graph[i][j] = cost;  graph[j][i] = cost;  }  prim(graph, m);  return 0;  
}  

编译运行结果:
普里姆结果



kruskal(克鲁斯卡尔)算法
时间复杂度:O(NlogN)(N为边数)
kruskal算法又称“加边法”,用于边数较少的稀疏图
方法:每次找图中权值最小的边,将边连接的两个顶点加入最小生成树集合中
注意:相同权值任选其中一个即可,但是不允许出现闭合回路的情况。
1
2
3
4
5
6
代码部分通过以下步骤可以得到最小生成树:

1.初始化:
构建边的结构体,包括起始顶点、终止顶点,边的权值
借用一个辅助数组vset[i]用来判断某边是否加入了最小生成树集合

#define MAXE 100
#define MAXV 100
typedef struct{int vex1;                     //边的起始顶点int vex2;                      //边的终止顶点int weight;                    //边的权值
}Edge;
void kruskal(Edge E[],int n,int e)
{ int i,j,m1,m2,sn1,sn2,k,sum=0;int vset[n+1];for(i=1;i<=n;i++)        //初始化辅助数组vset[i]=i;k=1;//表示当前构造最小生成树的第k条边,初值为1j=0;//E中边的下标,初值为0

2.取边和辅助集合更新
按照***排好的顺序***依次取边,若不属于同一集合则将其加入最小生成树集合,每当加入新的边,所连接的两个点即纳入最小生成树集合,为避免重复添加,需要进行辅助集合更新
注:由于kruskal算法需要按照权值大小顺序取边,所以应该事先对图按权值升序,这里我采用了快速排序算法,具体算法可以参照快速排序(C语言)

 while(k<e)//生成的边数小于e时继续循环{m1=E[j].vex1;m2=E[j].vex2;//取一条边的两个邻接点sn1=vset[m1];sn2=vset[m2];                           //分别得到两个顶点所属的集合编号if(sn1!=sn2)//两顶点分属于不同的集合,该边是最小生成树的一条边{//防止出现闭合回路 printf("V%d-V%d=%d\n",m1,m2,E[j].weight);sum+=E[j].weight;k++;                //生成边数增加if(k>=n)break;for(i=1;i<=n;i++)    //两个集合统一编号if (vset[i]==sn2)  //集合编号为sn2的改为sn1vset[i]=sn1;}j++;                  //扫描下一条边}printf("最小权值之和=%d\n",sum);
}

具体算法实现:

#include <stdio.h>
#define MAXE 100
#define MAXV 100
typedef struct{int vex1;                     //边的起始顶点int vex2;                      //边的终止顶点int weight;                    //边的权值
}Edge;
void kruskal(Edge E[],int n,int e)
{ int i,j,m1,m2,sn1,sn2,k,sum=0;int vset[n+1];for(i=1;i<=n;i++)        //初始化辅助数组vset[i]=i;k=1;//表示当前构造最小生成树的第k条边,初值为1j=0;//E中边的下标,初值为0while(k<e)//生成的边数小于e时继续循环{m1=E[j].vex1;m2=E[j].vex2;//取一条边的两个邻接点sn1=vset[m1];sn2=vset[m2];                           //分别得到两个顶点所属的集合编号if(sn1!=sn2)//两顶点分属于不同的集合,该边是最小生成树的一条边{//防止出现闭合回路 printf("V%d-V%d=%d\n",m1,m2,E[j].weight);sum+=E[j].weight;k++;                //生成边数增加 if(k>=n)break;for(i=1;i<=n;i++)    //两个集合统一编号if (vset[i]==sn2)  //集合编号为sn2的改为sn1vset[i]=sn1;}j++;                  //扫描下一条边}printf("最小权值之和=%d\n",sum);
}
int fun(Edge arr[],int low,int high){int key;Edge lowx;lowx=arr[low];key=arr[low].weight;while(low<high){while(low<high && arr[high].weight>=key)high--;if(low<high)arr[low++]=arr[high];while(low<high && arr[low].weight<=key)low++;if(low<high)arr[high--]=arr[low];}arr[low]=lowx;return low;} 
void quick_sort(Edge arr[],int start,int end)
{int pos;if(start<end){pos=fun(arr,start,end);quick_sort(arr,start,pos-1);quick_sort(arr,pos+1,end);}
}
int main()
{Edge E[MAXE];int nume,numn;//freopen("1.txt","r",stdin);//文件输入printf("输入顶数和边数:\n");scanf("%d%d",&numn,&nume);for(int i=0;i<nume;i++)scanf("%d%d%d",&E[i].vex1,&E[i].vex2,&E[i].weight);quick_sort(E,0,nume-1);kruskal(E,numn,nume);
}

编译运行结果:
克鲁斯卡尔结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569888.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

屏蔽烦人的百度搜索热点

开发时&#xff0c;在百度上搜索一些资料时&#xff0c;总是不经意看到百度搜索热点的一些新闻&#xff0c;然后经不住诱惑的标题就不经意间点了进去&#xff0c;然后就是连锁反应了。。等再次回到开发时已经是半小时之后的事了。又要把想要的东西重新捋一遍&#xff0c;导致开…

矩阵在竞赛中的应用

矩阵乘法代码&#xff1a; struct matrix {int n, m;int a[100][100]; }; // A.m B.n matrix matrix_mul(matrix A, matrix B) {matrix C;C.n A.n;C.m B.m;for (int i 0; i < A.n; i) {for (int j 0; j < B.m; j) {C.a[i][j] 0;for (int k 0; k < A.m; k) {C.a…

连通图遍历策略之广度优先搜索(C语言)

广度优先搜素&#xff08;BFS&#xff09; 广度优先搜索(又称宽度优先搜索)算法是最简便的图的搜索算法之一&#xff0c;该算法属于一种盲目搜寻法&#xff0c;目的是系统地展开并检查图中的所有节点&#xff0c;以找寻结果。换句话说&#xff0c;它并不考虑结果的可能位置&am…

多条件组合查询

一、效果图 二、前端代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>Jquery分类</title> <script src"http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></sc…

“三次握手,四次挥手”你真的懂吗?

记得刚毕业找工作面试的时候&#xff0c;经常会被问到&#xff1a;你知道“3次握手&#xff0c;4次挥手”吗&#xff1f;这时候我会“胸有成竹”地“背诵”前期准备好的“答案”&#xff0c;第一次怎么怎么&#xff0c;第二次……答完就没有下文了&#xff0c;面试官貌似也没有…

连通图遍历策略之深度优先搜索(C语言)

深度优先搜素&#xff08;DFS&#xff09; 深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中&#xff0c;当一个超链被选择后&#xff0c;被链接的HTML文件将执行深度优先搜索&#…

PHP+cURL扩展的使用

PHP 支持 Daniel Stenberg 创建的 libcurl 库&#xff0c;能够连接通讯各种服务器、使用各种协议。libcurl 目前支持的协议有 http、https、ftp、gopher、telnet、dict、file、ldap。 libcurl 同时支持 HTTPS 证书、HTTP POST、HTTP PUT、 FTP 上传(也能通过 PHP 的 FTP 扩展完…

dwc_otg驱动 BUG: sleeping function called from invalid context at mm/page_alloc.c

方案商的开发板上otg功能只能做device&#xff0c;硬件看过后说没有5v供电&#xff0c;加上后能够识别U盘了&#xff0c;但是内核报了错 [ 3.264000] usb 2-1: new high-speed USB device number 2 using dwc_otg[ 3.280000] BUG: sleeping function called from invalid conte…

单源最短路径之迪杰斯特拉算法(C语言)

Dijkstra&#xff08;迪杰斯特拉&#xff09;算法 采用广度优先搜索思想&#xff0c;对有向赋权图寻找最短路径。 该算法对于不含负权的有向图来说&#xff0c;是目前已知的最快的单源最短路径算法。 时间复杂度&#xff1a;O&#xff08;n^2&#xff09; 基本原理&#xf…

PHP_VERSION获取php版本

PHP_VERSION PHP_VERSION&#xff1a;当前PHP的版本号。 <?phpecho PHP_VERSION; ?>结果&#xff1a;5.4.45 很多时候我们只需要获取大的版本号就够用了&#xff0c;因为大的版本号决定了很多新的特性和函数。因此用substr()函数获取前三位字符。 <?phpecho su…

全源最短路径之弗洛伊德算法(C语言)

Floyd&#xff08;弗洛伊德&#xff09;算法 该算法是解决任意两点间的最短路径的一种算法&#xff0c;可以正确处理有向图或负权&#xff08;但不可存在负权回路)的最短路径问题&#xff0c;同时也被用于计算有向图的传递闭包。 时间复杂度为 O&#xff08;N^3&#xff09; …

Mybatis主线流程源码解析

Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细&#xff0c;但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题&#xff0c;需要花费大量的时间解决。 抽空了解一下Mybatis的相关源码还是很有必要。 先来看一个简单的Demo&#xff1a; …

动态规划之背包问题(C语言)

动态规划 动态规划&#xff08;英语&#xff1a;Dynamic programming&#xff0c;简称DP&#xff09;是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题 动态规划思想大致上为&#xff1a;若要解一…

PHP函数-判断字符是否在于指定的字符串中

php中判断一个字符是否在字符串中 1、函数 以下四个函数都可以用来判断字符串中是否存在待查询的字符&#xff0c;可以是数字、字母或者符号。 strpos() - 查找字符串在另一字符串中第一次出现的位置&#xff08;区分大小写&#xff09; stripos() - 查找字符串在另一字符…

最大子列和问题(JAVA)

最大子列和 问题描述&#xff1a;给定N个整数的序列{A1&#xff0c;A2&#xff0c;A3&#xff0c;…&#xff0c;An}&#xff0c;求解子列和中最大的值。 这里我们给出{-2&#xff0c;11&#xff0c;-4&#xff0c;13&#xff0c;-5&#xff0c;-2}这样一个序列&#xff0c;正…

Oracle 11g必须开启的服务及服务详细介绍

成功安装Oracle 11g数据库后&#xff0c;你会发现自己电脑运行速度会变慢&#xff0c;配置较低的电脑甚至出现非常卡的状况&#xff0c;通过禁止非必须开启的Oracle服务可以提升电脑的运行速度。那么&#xff0c;具体该怎么做呢? 按照win7 64位环境下Oracle 11g R2安装详解中…

PHP—str_replace()替换函数的使用

一、str_replace()函数 1、定义和用法 str_replace() 函数替换字符串中的一些字符&#xff08;区分大小写&#xff09;。   注释&#xff1a;该函数是区分大小写的。请使用 str_ireplace() 函数执行不区分大小写的搜索。   注释&#xff1a;该函数是二进制安全的。 2、语…

JS/jQuery获取input的值和清空input的value值

一、获取input的值 1、通过普通选择器获取   通过类选择器获取&#xff1a;$(.class).val()   通过id选择器获取&#xff1a;$(" #id").val() 2、通过标签选择器获取   $(" input[ namename ] ").val()   $(" input[ typetext ] ").val…

JAVA刷题方法整理

JAVA刷题方法整理 一、String—>String[] 利用String.split()实现 注&#xff1a;在使用String.split 方法分隔字符串时&#xff0c;分隔符如果用到一些特殊字符&#xff0c;可能会得不到我们预期的结果&#xff0c;例如“|”&#xff0c;“*”&#xff0c;“”等&#x…

实现多线程Callable接口

Callable接口&#xff0c;实现多线程 1.实现 Callable接口&#xff0c;相较于实现 Runnable 接口的方式&#xff0c;优点是&#xff1a;方法可以有返回值&#xff0c;并且可以抛出异常 2.需要 FutureTask实现类的支持&#xff0c;用于接收运算结果 3.result.get()&#xff0c;接…