热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构克鲁斯卡尔算法,普利姆算法(求最小生成树)

当然了昨天晚上写了求两源点之间最少权值和,就不得不再写一下另外两个求最小生成树的算法分别是克鲁斯卡尔和普利姆了,话不多说直接进入主题目录并查集

当然了昨天晚上写了求两源点之间最少权值和,就不得不再写一下另外两个求最小生成树的算法分别是克鲁斯卡尔和普利姆了,话不多说直接进入主题


目录

并查集:

克鲁斯卡尔算法(与并查集结合起来):

普利姆算法:




并查集:

为啥先说并查集,因为对我们下面看克鲁斯卡尔有用,先引入并查集的例子进行初步讲解

首先什么是并查集:其实通俗来讲就是将一系列有特定关系的事务给他总合起来

举一个简单例子Eg:比如a跟b有关系,b是a的父亲,然后a和c是兄妹关系,那么b也就是c的父亲 

所以就是按图表示为

第一种:a->b,a-c(平级)=>c->b

第二种:a->b,b->c=>a->b->c

第三种:a->b->e,c->e=>(a,c,b)->e

这就是大体理解一下,为了做到上面的效果我们需要两个数组来存储,第一个用来存储每一个节点的祖先分别是什么,第二个用来存储当前节点的祖先下面有几个子节点

先看初始化:

void Init(int n)
{for(int i=1; i<=n; i++){arr[i]=i; //arr是当前节点的祖先节点,所以刚开始每个人的祖先节点都是自己本身rank[i]=1; //当前下面的子节点只有本身节点所以就为1}
}

然后就是寻找祖先节点:

int find(int x)
{if(x==arr[x]) //如果是当前节点的祖先就是自己那就直接返回{return x;}else{return arr[x]=find(arr[x]); //如果是其他节点,就继续去寻找其他节点直到找到最终的祖先节点}
}

然后将节点进行结合:

void unite(int x,int y)
{x=find(x); //首先找两个节点的祖先节点y=find(y);if(x==y) //如果祖先是同一个节点那就不需要结合{ return;}if(rank[x]}

 


克鲁斯卡尔算法(与并查集结合起来):

众所周知啊克鲁斯卡尔(Kruskal)算法就是用边权值进行寻找最小生成树,先将边的最小值进行一个排序然后从最小值开始寻找生成一颗完整的树

但是存在一个问题就是说什么情况下可以称作为一颗完整的树呢?

答案很简单我们用并查集来解决找祖先的问题。

所以我们先将整个已知条件进行按权值排序,这里用啥都行,希尔,快排,归并,冒泡都可以

我用的冒泡(简单一点):

void sort(int m)
{Node N;for(int i=1; i<=m; i++){for(int j=i; j<=m; j++){if(a[i].e>a[j].e){N=a[i];a[i]=a[j];a[j]=N;}}}
}

然后就是实现克鲁斯卡尔算法了:

int kruskal(int n,int m)
{int result=0,n_edge=0,f1,f2,j=1;for(int i=1; i<=m; i++){f1=find(arr[a[i].edga_first]); //先找两人的共同祖先f2=find(arr[a[i].edga_second]);if(f1!=f2) //一样的话就不用算了{unite(f1,f2); //不一样直接合并因为我们已经按最小权值进行了排序result+=a[i].e; //将当前的总权值进行跟新n_edge++; //表明当前的树有几个节点}if(n_edge==n-1) //如果节点数已经够了总结点就可以退出了{return result;}}return -1; //如果无法生成最小数就返回-1
}

看看整体实现

#include
#include
#include
#define max 200005
typedef struct graph //定义一个结构体保存边边权三个值
{int edga_first,edga_second;int e;
} Node;
int arr[max],rank[max]; //arr为祖先节点的定义,rank为当前祖先节点的子节点数
Node a[max];
void sort(int m)
{Node N;for(int i=1; i<=m; i++){for(int j=i; j<=m; j++){if(a[i].e>a[j].e){N=a[i];a[i]=a[j];a[j]=N;}}}
}
void Init(int n)
{for(int i=1; i<=n; i++){arr[i]=i;rank[i]=1;}
}
int find(int x)
{if(x==arr[x]){return x;}else{return arr[x]=find(arr[x]);}
}
void unite(int x,int y)
{x=find(x);y=find(y);if(x==y){return;}if(rank[x]}
int kruskal(int n,int m)
{int result=0,n_edge=0,f1,f2,j=1;for(int i=1; i<=m; i++){f1=find(arr[a[i].edga_first]);f2=find(arr[a[i].edga_second]);if(f1!=f2){unite(f1,f2);result+=a[i].e;n_edge++;}if(n_edge==n-1){return result;}}return -1;
}
int main()
{int n,m,p,q,ans;printf("请输入当前的节点总数和已知条件数:\n"); scanf("%d%d",&n,&m);Init(n);printf("分别按照边1,边2,权值的方式进行输入,总共输入%d条:\n",m);for(int i=1; i<=m; i++){scanf("%d%d%d",&a[i].edga_first,&a[i].edga_second,&a[i].e);}sort(m);p=kruskal(n,m);if(p==-1){printf("无法生成最小生成树\n");}else{printf("最小生成树的总权值为:%d\n",p);}return 0;
}/*
测试样例
6 10
1 3 4
1 2 3
1 6 1
6 2 5
6 5 3
2 3 2
2 4 4
5 4 7
4 3 6
5 2 5
*/

给一个样例图:

 

 

最小生成树如下:

 

 

 代码实测运行图:

 


普利姆算法:

 


推荐阅读
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Java数组的定义、初始化和多维数组的用法。通过动态初始化和静态初始化两种方式来初始化数组,并讨论了数组的内存分配和下标的特点。同时详细介绍了Java二维数组的概念和使用方法。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
多米音乐_54101533
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有