算法基础 - 最小生成树(Kruscal算法)
来源:互联网 发布:网络模块接a还是b 编辑:程序博客网 时间:2024/06/07 06:40
最小生成树
就是让一个图里,生成一个树状图,任意两个节点只有唯一的路径到达,并且让这个距离最短。
Karuscal算法
这个算法是相对于Prim算法( 传送门:Prim算法 )的另外一个算法,这个算法主要的应用场景是对于稀疏图非常好用,因为Karuscal算法是根据边来计算的,每次添加一个最短的边。
而Prim的每步是添加一个最短的点,类似于Dijkstra算法的步骤。所以Prim的算法是对于稠密的图效果更好。
算法思想
首先对所有边进行一次排序,然后每次找到最短的边,假如这个边的两个点不属于同一个子树,就加入进来,如果属于同一棵子树,就找下一个边。
这里引用一下hihocoder上面的解释:
小Hi的温馨提示:边的费用在某些时候可以被理解成为边的长度、边的权值等等!所以在后文中各种可能会用上述各种称呼来描述费用一事。
小Ho听到了这个问题,发表了感慨:“这不就是之前最短路问题的时候针对点集变大,但是边集很小的稀疏图么?和SPFA算法当时遇到的问题很像诶!”
小Hi严肃道:“是的!在现实生活中,大部分的图其实都是稀疏图,哪怕不是,也可以像我这种通过筛选的方式转化为稀疏图!所以稀疏图上的问题是非常重要的!”
“是的!”小Ho连连称是,继续道:“那难道这题也像SPFA那样子来做么?但是最小生成树似乎是不可以用宽度优先搜索来解决的啊?”
“倒也没有那么复杂。”小Hi道:“还记的我们在Prim算法中得出的结论——对于城市i(i≠1),如果i与城市1的距离不超过其他任何城市j(j≠1)与城市1的距离,那么(1,
i)这一条边一定存在于某一棵最小生成树中么?”“自然记得。”
“我们来把这个结论稍微改一下:图中最短的边一定属于某棵最小生成树。”小Hi说道:“证明是简单的——因为城市1的标号是随意的,也就是无论给哪个城市标1都会有之前的结论,那么对于任意节点来说它连接的所有边中最短的边一定存在于某一棵最小生成树中,而整个图中最短的一条边一定是这样的一条边。”
小Ho道:“是的,那么也就是说我只需要不断的找到当前图中最短的一条边(一开始就将所有的边排序然后从小到大)——这样的一条边一定属于某棵最小生成树!然后我只需要将连接的2个节点合并成为一个新的节点,那么这个问题就变成了一个规模-1的问题了!只需要不断重复这样的操作,就能够使得问题最后变成1的规模,这个时候只需要之前找到的所有一定存在于最小生成树中的边的费用加起来就是答案了就可以了!”
小Hi点了点头,说道:“那么还剩一个问题,在Prim问题中,由于合并都是和1号城市合并,所以只需要简单的记录一个节点是否已经合并进了1号城市就可以了,但是在这里却会复杂很多,你有什么想法么?”
“你这就太小看我的记忆力了!”小Ho抱怨道:“在当年遇到黑叔叔的时候,我们不是学会了一种并查集的方法么?在这里只需要用并查集维护哪些节点被合并到了一起,不就行了?”
“嗯~ o( ̄▽ ̄)o,算你聪明,赶紧去实现程序吧!”
代码实现
#include <iostream>#include <vector>#include <cstring>#include <string>#include <unordered_map>using namespace std;#define MAXNUM 100005 //定义最大顶点个数#define MAXPATH 1000005 //定义最多边的条数unordered_map<int, int> parent;//利用并查集来判断两个点在不在同一棵子树上struct Path{ //定义边的结构体 int st; int en; int length; Path(int s, int e, int len): st(s), en(e), length(len){}};Path *path[MAXPATH];int cur = 0;int findParent(int n){ //并查集查找方法 if (parent[n] == n) { return n; } parent[n] = findParent(parent[n]); //状态压缩 return parent[n];}void quickSort(int left, int right){ //快排方便找最小的边 if (left >= right) { return; } int j = left; for (int i = left; i < right; i++) { if (path[i]->length < path[right]->length) { swap(path[i], path[j]); j++; } } swap(path[j], path[right]); quickSort(left, j-1); quickSort(j+1, right);}int findMin(int M){ //找最小边 int st,en, pst, pen; for (int i = cur; i < M; i++) { st = path[i]->st; en = path[i]->en; pst = findParent(st); pen = findParent(en); if (pst != pen) { parent[pst] = parent[pen]; cur = i+1; return path[i]->length; } } return -1;}long long Kruscal(int M){ long long result = 0; quickSort(0, M-1); while (1) { int len = findMin(M); if (len == -1) { break; } result += len; } return result;}int main(){ int M,N; cin>>M>>N; //M是顶点个数,N是边的数量 int st,en,len; for (int i = 0; i < N; i++) { scanf("%d%d%d", &st,&en,&len); path[i] = new Path(st,en,len); //每次输入起点,终点,边长 parent[st] = st; parent[en] = en; } cout<<Kruscal(N)<<endl; return 0;}
- 算法基础 - 最小生成树(Kruscal算法)
- kruscal最小生成树算法
- 最小生成树-Kruscal算法
- 最小生成树之kruscal算法
- 最小生成树(prim和kruscal算法)
- 最小生成树 Kruscal算法--poj 2377
- kruscal算法求最小生成树总结
- hiho1098 : 最小生成树·Kruscal算法
- Kruscal最小生成树算法(C++)
- HDU 1233最小生成树 Kruscal 算法
- 最小生成树 kruscal算法 C语言
- 最小生成树的prim算法和kruscal算法
- KRUSCAL算法求图的最小生成树(C实现)
- Kruscal算法+并查集 求解最小生成树
- Kruscal算法 并查集求解最小生成树
- poj3522-kruscal算法求最小生成树+枚举
- 并查集 求解最小生成树 Kruscal算法
- POJ 1789最小生成树(kruscal算法)
- C# WINFORM 强制让窗体获得焦点(可实现单次置顶或持续置顶)
- kvm 安ERROR Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory
- keil5不能识别自己写的头文件
- Qt调用qt生成的dll 显式和隐式(静态动态)
- CCNET+MSBuild+SVN实时构建的优化总结
- 算法基础 - 最小生成树(Kruscal算法)
- Android设计模式--适配器模式--Adapter入门
- 博客地址迁移啦!!!
- 雷达目标散射截面积(RCS)
- 一个优化过的OC的简单冒泡排序算法
- C语言宏定义
- shell运算符:算数运算符, 关系运算符,布尔运算符,字符串运算符,文件测试运算符
- extjs Form验证、表单验证、表单错误提示位置
- Android LayoutInflater原理分析,带你一步步深入了解View(一)