数据结构:最小生成树--Kruskal算法
Kruskal算法
Kruskal算法
求解最小生成树的还有一种常见算法是Kruskal算法。它比Prim算法更直观。从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条边不能使已有的边产生回路。
手动求解会发现Kruskal算法异常简单,以下是一个样例
先对边的权值排个序:1(0,1) 2(2,6) 4(1,3) 6(1,2) 8(3,6) 10(5,6) 12(3,5) 15(4,5) 20(0,1)
首选边1(0,1)、2(2,6)、4(1,3)、6(1,2),此时的图是这样
显然,若选取边8(3,6)会出现环,则必须抛弃8(3,6),选择下一条10(5,6)没有问题。此时图变成这样
显然,12(3,5)相同不可取,选取15(4,5),边数已达到要求,算法结束。终于的图是这种
算法逻辑人非常easy理解。但用代码推断当前边是否会引起环的出现。则非常棘手。
算法说明
为了推断环的出现,我们换个角度来理解Kruskal算法的做法:初始时,把图中的n个顶点看成是独立的n个连通分量,从树的角度看,也是n个根节点。我们选边的标准是这种:若边上的两个顶点从属于两个不同的连通分量。则此边可取,否则考察下一条权值最小的边。
于是问题又来了,怎样推断两个顶点是否属于同一个连通分量呢?这个能够參照并查集的做法解决。它的思路是:假设两个顶点的根节点是一样的,则显然是属于同一个连通分量。
这也相同暗示着:在增加新边时,要更新父节点。
详细细节看代码:
代码
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXWEIGHT 100
/*
全局变量
numV顶点数
numE边数
*/
int numV, numE;
//边
typedef struct edge_tag
{
int tail;
int head;
int weight;
}Edge;
//检測边
bool checkEdge(int tail, int head, int weight)
{
if (tail == head
|| tail < 0 || tail >= numV
|| head < 0 || head >= numV
|| weight <= 0 || weight >= MAXWEIGHT)
return false;
return true;
}
//输入边
void inputEdge(Edge *edge, int numE)
{
int i, tail, head, weight;
cout << "输入边的起点、终点和权值" << endl;
i = 0;
while (i < numE)
{
cin >> tail >> head >> weight;
while (!checkEdge(tail, head, weight))
{
cout << "输入错误!又一次输入" << endl;
cin >> tail >> head >> weight;
}
edge[i].tail = tail;
edge[i].head = head;
edge[i].weight = weight;
i++;
}
}
int cmp(const void *edge1, const void *edge2)
{
return ((Edge*)edge1)->weight - ((Edge*)edge2)->weight;
} //并查集的常见操作
/*
压缩查找
查找child的根节点
*/
int Find(int child, int *parent)
{
if (parent[child] == child)
return child;
parent[child] = Find(parent[child], parent); //压缩路径
return parent[child];
}
//合并
bool Union(Edge *e, int *parent, int *childs)
{
//处于同一个棵树中。则不能合并。否则会出现环
int root1, root2;
root1 = Find(e->tail, parent);
root2 = Find(e->head, parent);
if (root1 != root2)
{
//把小树合并到大树根下
if (childs[root1] > childs[root2])
{
parent[root2] = root1;
childs[root1] += childs[root2] + 1;
}
else
{
parent[root1] = root2;
childs[root2] += childs[root2] + 1;
}
return true;
}
return false;
}
/*
Kruskal算法
求最小生成树
*/
void Kruskal(int numV, int numE)
{
//边的集合
Edge *edge = new Edge[numE];
inputEdge(edge, numE);
/*
parent[i]是顶点i的父顶点
childs[i]是顶点i的孩子数
child的复数是children,这里的childs是杜撰的
*/
int *parent = new int[numV];
int *childs = new int[numV];
//初始化
for (int i = 0; i < numV; i++)
{
/*
初始时。每个顶点都是根。且无孩子
把每个顶点的的父节点设置为自身下标,也是标明类别
*/
parent[i] = i;
childs[i] = 0;
}
//对边按权值进行从小到大排序
qsort(edge, numE, sizeof(Edge), cmp);
int i, count;
i = count = 0;
cout << "最小生成树的边..." << endl;
while (i < numE)
{
if (Union(&edge[i], parent, childs))
{
cout << edge[i].tail << "---" << edge[i].head << endl;
count++;
}
if (count == numV - 1) //边数达到要求
break;
i++;
}
if (count != numV - 1)
cout << "此图为非连通图! 无法构成最小生成树。" << endl;
delete[]edge;
delete[]parent;
delete[]childs;
}
//检測顶点数和边数
bool checkVE(int numV, int numE)
{
if (numV <= 0 || numE <= 0 || numE > numV*(numV - 1) / 2)
return false;
return true;
}
int main()
{
cout << "******Kruskal***by David***" << endl;
cout << "输入顶点数、边数 ";
cin >> numV >> numE;
while (!checkVE(numV, numE))
{
cout << "输入数据有问题!又一次输入 ";
cin >> numV >> numE;
}
cout << endl << "Kruskal..." << endl;
Kruskal(numV, numE);
system("pause");
return 0;
}
执行
转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/38414683
若有所帮助,顶一个哦。
专栏文件夹:
数据结构:最小生成树--Kruskal算法的更多相关文章
- 【转】最小生成树——Kruskal算法
[转]最小生成树--Kruskal算法 标签(空格分隔): 算法 本文是转载,原文在最小生成树-Prim算法和Kruskal算法,因为复试的时候只用到Kruskal算法即可,故这里不再涉及Prim算法 ...
- 模板——最小生成树kruskal算法+并查集数据结构
并查集:找祖先并更新,注意路径压缩,不然会时间复杂度巨大导致出错/超时 合并:(我的祖先是的你的祖先的父亲) 找父亲:(初始化祖先是自己的,自己就是祖先) 查询:(我们是不是同一祖先) 路径压缩:(每 ...
- 数据结构之最小生成树Kruskal算法
1. 克鲁斯卡算法介绍 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法. 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路. 具体做法:首先构造一个 ...
- 并查集与最小生成树Kruskal算法
一.什么是并查集 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题.有一个联合-查找算法(union-find algorithm)定义了两个用于次数据结构的操作: Fi ...
- 最小生成树——Kruskal算法理解
背景:本文是在小甲鱼数据结构教学视频中的代码的基础上,添加详细注释而完成的.该段代码并不完整,仅摘录了核心算法部分,结合自己的思考,谈谈理解. Prim算法理解: 如图(摘录自小甲鱼教学视频中的图片) ...
- 最小生成树——kruskal算法
kruskal和prim都是解决最小生成树问题,都是选取最小边,但kruskal是通过对所有边按从小到大的顺序排过一次序之后,配合并查集实现的.我们取出一条边,判断如果它的始点和终点属于同一棵树,那么 ...
- 最小生成树Kruskal算法
Kruskal算法就是把图中的所有边权值排序,然后从最小的边权值开始查找,连接图中的点,当该边的权值较小,但是连接在途中后会形成回路时就舍弃该边,寻找下一边,以此类推,假设有n个点,则只需要查找n-1 ...
- 最小生成树------Kruskal算法
Kruskal最小生成树算法的概略描述:1 T=Φ:2 while(T的边少于n-1条) {3 从E中选取一条最小成本的边(v,w):4 从E中删去(v,w):5 if((v,w)在T中不生成环) { ...
- 求最小生成树——Kruskal算法
给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这篇文章先介绍Kruskal算法. Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺 ...
随机推荐
- python fromkeys的坑
有个不定长的列表,想把列表中的每个值当做字典的key, 初始值为空列表,于是想到了fromkeys这个方法 In [337]: l = ['a','b','c'] In [338]: res = di ...
- Eclipse下搭建Maven框架
内容中包含 base64string 图片造成字符过多,拒绝显示
- AC日记——营业额统计 codevs 1296 (splay版)
营业额统计 思路: 每次,插入一个点: 然后找前驱后继: 来,上代码: #include <cmath> #include <cstdio> #include <iost ...
- K皇后问题递归解法
#include<iostream> #include<cmath> #include<ctime> using namespace std; bool che ...
- Android开发 大坑Fragment
是不是弄了半天你的Fragment老是Replace不了,我的原因是:弄成静 态的,然后要动态Replace,竟然不行,后来框个FragmentLayout,改成全动态添加和Replace,OK了.
- (5)centos图形界面安装
1.登录 2.先安装MATE可视化桌面 yum groups install "MATE Desktop" 选择y 3.安装X Window System:图形接口 yum gro ...
- ansible 2.7.1 常见错误总结
1.RequestsDependencyWarning (refer to http://blog.51cto.com/mjunetwslinux/2177727?source=dra) python ...
- Java NIO中的Channel接口
1. Channel 通道,可以将指定文件的部分或全部直接映射成Buffer. 不能直接读写Channel中的数据,Channel只能与ByteBuffer交互. 读数据时,把Channel中的数据 ...
- 2016北京集训测试赛(十)Problem A: azelso
Solution 我们把遇到一个旗子或者是遇到一个敌人称为一个事件. 这一题思路的巧妙之处在于我们要用\(f[i]\)表示从\(i\)这个事件一直走到终点这段路程中, \(i\)到\(i + 1\)这 ...
- jquery_final
第一章 jquery入门 1,jquery的引入 <script type="text/javascript" src="js/jquery-3.3.1.min.j ...