数据结构题:克鲁斯卡尔(Kruscal)算法求最小生成树

简介: 数据结构题:克鲁斯卡尔(Kruscal)算法求最小生成树

前言


问题描述:用克鲁斯卡尔算法求无向网图的最小生成树。本文编程软件是Visual Studio 2019,使用的是C语言进行课程设计。


提示:以下是本篇文章正文内容,下面案例可供参考。


一、目的与要求


设计目的


  1. 该课题的源码必须能够调试成功;
  2. 提供一个main函数完成的程序源码版本。基本数据结构有详细说明,每个功能函数有详细说明;
  3. 全文关键代码须加上注释。


设计要求

  1. 生成一个无向网图;
  2. 要求采用邻接矩阵或链接表存储来完成;
  3. 最后打印输出最小生成树;
  4. 每一个函数要有必要的注释,在课程设计中有流程图。


二、原理及方案


1.克鲁斯卡尔(Kruskal)算法


克鲁斯卡尔算法的思想是:假设G=(V,E)是一个具有n给顶点的连通网,T=(U,TE)是G的最小生成树。U的初值等于V,即拥有G中的全部顶点。算法开始时,TE的初值为空值。将图G中的边按权值从小到大的顺序依次选取,若选取的边使生成树T不形成回路,则把它并入TE中,保留作为T的一条边;若选取的边使生成树T形成回路,则将其舍弃,如此进行下去,直到TE中包含n-1条边为止,此时的T即为最小生成树。

🎈这里我们通过一个例子了解如何对一个无向带权图采用克鲁斯卡尔算法求其最小生成树:


例,对于下图所示的无向带权图,采用克鲁斯卡尔算法求最小生成树的过程为:

1666884195089.jpg

通过该图,可知其中的边按权值由小到大的顺序是:

从顶点5开始,连接5和4,权值为1

1666884205837.jpg

再连接3,4,权值为2,此时选取的边使生成树T形成回路,舍弃;从另一边连接5,0,权值为3;再连接0,2,权值为4,形成回路舍弃;连接5,1,权值为5,如下图,即得到最小生成树:

1666884217204.jpg

步骤 生成树中各顶点集合 生成树的边 生成树的权值
初始状态 {0},{1},{2},{3},{4},{5}
1 {0},{1},{2},{3},{4,5} (4,5) 1
2 {0},{1},{2},{3,4,5} (3,4) 2
3 {1},{2},{0,3,4,5} (0,5) 3
4 {1},{2,0,3,4,5} (0,2) 4
5 {1,2,0,3,4,5} (1,5) 5


实现该算法需要设一个边集合数组E,其中包括边的起始点u,终止点v和这条边的权值w。首先编写函数CreateEdge循环建立一个边集合E,并返回边的数目n。因为克鲁斯卡尔算法要求边集必须是按从小到大的顺序排好,所以编写函数seeks实现对数组E进行排序的功能,函数sort排序的结果是E中各边按从小到大排好序。

克鲁斯卡尔算法的编程思路是首先要用到辅助数组set,用来存放各顶点所在的最小生成树顶点集合,然后从边集E中顺序取出各条边,判断该边的两个顶点是否在同一集合中(需要编写一个判断顶点所在集合的函数seeks),若不在同一集合,则该边为最小生成树的一条边,输出该条边的顶点序列和权值,并在set数组中将顶点v2加到顶点v1集合中,重复以上操作直到所有顶点都在一个集合中结束。


2.方案思路


Prim算法是找点,而我们所用的Kruscal算法则是从边出发。

具体思路:

1.把所有的边和这条边代表的权值用一个数组存储起来,并按权值大小给数组排序(升序)。

2.按顺序从数组中拿出一条边,检查这条边是否与到目前为止形成的树构成环,如果形成了环,就丢弃它;如果没有,就把这条边加入树中。

3.重复步骤2,直到树中有 n-1 条边为止。


三、设计过程


程序中无向网采用邻接矩阵存储,c程序如下:

//克鲁斯卡尔(Kruscal)算法求最小生成树
#include <stdio.h>
#define MAX 100       //定义最大顶点数
typedef struct        //建立一个边集合数组,其中包括边的起始点、终止点以及这条边的权值
{
  int u;         //一条边的起始顶点
  int v;         //一条边的终止顶点
  int w;         //边的权值(权重)
}Edge;             //边的类型定义
Edge E[MAX];       //定义一个全局数组E,用于存储图的各条边(创建边的数组)     
//创建一个无向网图
int CreateEdge()
{
  int i;
  int anum;
  printf("\n输入无向网的边数:");
  scanf_s("%d", &anum);
    for (i = 0; i < anum; ++i)
  {
    printf("\n输入第%d条边的起始顶点、终止顶点以及该边的权值(v1、v2、w):\n",i+1);
    scanf_s("%d %d %d", &E[i].u, &E[i].v, &E[i].w);
  }
  return anum;
}
//对边表进行从小到大排序算法
void sort(int n)
{
  int i, j;
  Edge t;
  for (i = 0; i < n-1; i++)
    for(j=i+1;j<n;j++)
      if(E[i].w>E[j].w)
  {
        t = E[i];
        E[i] = E[j];
        E[j] = t;
  }
}
//在边表中查看顶点v在哪个连通集合中
int seeks(int set[], int v)
{
  int i = v;
  while (set[i] > 0)
    i = set[i];
  return(i);
}
//克鲁斯卡尔算法的核心环节
void Kruskal(Edge E[], int n)    //算法核心环节
{
  int set[MAX];                //创建状态临时数组(辅助标志数组)
  int v1, v2, i;               //数组中的下标的临时变量
  for (i = 0; i < MAX; i++)
    set[i] = 0;              //给set数组中的每个元素赋予初值
  i = 0;                       //i表示特获取的生成树种的边数,初值为1
  while(i<n)                   //按边权递增顺序,逐边检查该边是否应加入到生成树中                             
  {
    v1 = seeks(set, E[i].u);    //确定顶点v所在的连通集
    v2 = seeks(set, E[i].v);
    if (v1!=v2)              //当v1,v2不在同一顶点集合,确定该边应当选入生成树
    {
      printf("(%d,%d) %d\n",E[i].u,E[i].v,E[i].w);
      set[v1]=v2;          //将v2加入到v1的集合中
    }
    i++;
  }
}
void main()
{
  int n;
  n=CreateEdge();                    //调用生成边表函数
  if(n>0)                            //判断输入的n值是否合法(n>0)
  {
    sort(n);                       //对边表集合进行排序
      printf("\n最小生成树为( (顶点,顶点) 权值 ):\n");
  }
  else
    printf("\n输入边数错误,请重新输入!\n");
  Kruskal(E,n);                      //调用克鲁斯卡尔算法求最小生成树
}


四、设计结果


1666884314783.jpg


五、例题测试


刚刚更新,我们通过一道题由以上代码可解决,直接上张图:

1666884330128.jpg

1666884393244.jpg


总结


以上就是今天要讲的内容,本文利用克鲁斯卡尔(Kruscal)算法求最小生成树中(无向网采用邻接矩阵存储),最后我们要知道克鲁斯卡尔算法的时间复杂度是主要由排序方法决定的,其排序方法只与网中边的条数有关,而与网中顶点的个数无关,当使用时间复杂度为O(elog2e)的排序方法时,克鲁斯卡尔算法的时间复杂度即为O(log2e),因此当网的顶点个数较多、而边的条数较少时,使用克鲁斯卡尔算法构造最小生成树效果较好。


结语


本文参考《数据结构》–上海交大

如有错误,欢迎指正!


相关文章
|
4月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
128 1
|
5月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
135 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
1月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
95 29
|
1月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
107 25
|
1月前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
77 23
|
2月前
|
存储 传感器 算法
使用贪心算法解决最小生成树问题
大家好,我是V哥。今天聊聊贪心算法解决最小生成树问题。面试中遇到此类题目,需掌握Prim和Kruskal算法。Prim适合稠密图,Kruskal适合稀疏图。两者时间复杂度分别为O(m log n)和O(m log m),各有优缺点。应用场景广泛,包括图像处理、传感器网络、社交网络分析等。关注V哥,全栈之路一起走。
|
2月前
|
算法 C++
【C++数据结构——图】最小生成树(头歌实践教学平台习题) 【合集】
【数据结构——图】最小生成树(头歌实践教学平台习题)目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:【合集】任务描述 本关任务:编写一个程序求图的最小生成树。相关知识 为了完成本关任务,你需要掌握:1.建立邻接矩阵,2.Prim算法。建立邻接矩阵 上述带权无向图对应的二维数组,根据它建立邻接矩阵,如图1建立下列邻接矩阵。注意:INF表示无穷大,表示整数:32767 intA[MAXV][MAXV];Prim算法 普里姆(Prim)算法是一种构造性算法,从候选边中挑
49 10
|
2月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
61 2
|
4月前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
122 33
|
4月前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?