最小生成树——Kruskal(克鲁斯卡尔)算法
【0】README
0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 Kruskal(克鲁斯卡尔)算法  的idea 并用 源代码加以实现;
0.2)最小生成树的基础知识,参见 http://blog.csdn.net/pacosonswjtu/article/details/49947085
【1】 Kruskal 算法(使用到了不相交集ADT的union/find 操作)
1.1)第二种贪婪策略是: 连续地按照最小的权选择边, 并且当所选的边不产生圈时就可以吧它作为取定的边;
1.2)形式上, Kruskal算法是在处理一个森林——树的集合。 开始的时候, 存在 |V|颗单节点树, 而添加一边则将两棵树合并成一颗树, 当算法终止的时候就只有一棵树了, 这颗树就是最小生成树;
1.3)Kruskal算法的执行步骤, 如下图所示,看个荔枝:
对上图的分析(Analysis):
- A1)当添加到森林中的边足够多时算法终止, 实际上, 算法就是要决定边(u, v)应该添加还是放弃。(前一章节中的 Union/Find 算法在这里适用)
1.4)我们用到了一个恒定的事实:在算法实施的任意时刻,两个顶点属于同一个集合当且仅当它们在当前的森林中连通;因此, 每个顶点最初是在它自己的集合中;
- 1.4.1) 如果u 和 v 在同一个集合中, 那么连接它们的边就要放弃, 因为由于它们已经连通 了,如果再添加一条边(u, v)就会形成一个圈了。
- 1.4.2)如果这两个顶点不在同一个集合中, 则将该边加入, 并对包含顶点u 和 v 的这两个集合实施一次合并。
- 1.4.3)容易看到,这样将保持集合不变性, 因为一旦边(u, v)添加到生成森林中, 若w连通到 u 而 x连通到v, 则x 和 w 必然是连通的, 因此属于相同集合 ;
1.5)固然,将边排序可便于选取,不过,用线性时间建立一个堆则是更好的想法;
- 1.5.1)此时, deleteMin 将使得边依序得到测试。 典型情况下, 在算法终止前只有一小部分边需要测试, 尽管测试所有的边的情况是有可能的;例如,还有一个顶点 v8以及值为100的边(v5, v8),那么所有的边都会要考察到;
1.6)因为一条边由3个部分的数据组成, 所以在某些机器上吧优先队列实现成指向边的指针数组比实现成边的数组更为有效。
- 1.6.1)这种实现的 效果在于, 为重新排列堆, 需要移动的只有那些指针, 而大量的记录则不必移动;
1.7)时间复杂度:该算法的最坏情形运行时间为 O(|E|log|E|), 它受堆操作控制。 注意, 由于 |E|=O(|V|^2), 因此这个运行时间实际上是 O(|E|log|V|)。在实践中, 该算法要比这个时间界指示的时间快得多;
【2】source code + printing results(将我的代码打印结果 同 "1.3" 上图中的手动模拟的 Kruskal 算法的结果进行比较,你会发现, 它们的结果完全相同,这也证实了我的代码的可行性)
2.0)code specification:
- s1)本代码采用了优先队列(二叉小根堆)来升序选取边;
- s2)本代码采用了用到了不相交集ADT的 find和setUion 操作来对边的两个vertexes 进行union 操作以及更新它们的根;
- s3)对于根的初始化,我是这样初始化的—— parent[0]=-1,parent[1]=-2, parent[2]=-3, parent 说白了,就是 set的 一个 index, 所以开始肯定是不一样的; 然后,在union的时候,我只要检查是否 i == -parent[i]-1 就可以知道 它是否是树的根;
- s4) 在合并的时候,要对边的两个顶点 start 和 end 的 parent做update, 这里涉及到4种情况—— start为根且end不为根;start为根且end为根;start为不为根且end为根;start不为根且end不为根; (干货,本代码的重中之重以及新颖之处)
2.1)download source code: https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p240_kruskal
2.2)source code at a glance(for complete code , please click the given link above):
#include <stdio.h>
#include "binaryheap.h"
// allocate memory for the vertexes with size
Vertex* makeEmptyVertexes(int size)
{
	Vertex *array;
	int i;
	array = (Vertex*)malloc(size * sizeof(Vertex));
	if(!array)
	{
		Error("out of space, from func makeEmptyVertexes");
		return NULL;
	} 
	// initializing the set index towards every vertex with its array index
	for(i = 1; i <= size; i++)
		array[i-1] = -i;
	return array;
}
void printParent(Vertex* vertexes, int size)
{
	int i;
	printf("\n\n\t the parent of every vertex at a glance");
	for(i=0; i<size; i++)
		printf("\n\t parent[%d] = %d", i, vertexes[i]);
}
int find(Vertex *parent, Vertex single)
 {
	while (single >= 0)
		single = parent[single];
	return single;
}
//judge whether the vertex index is the parent or not, also 1 or 0
//if the vertex under index is not the parent ,that's to say its parent is one of other vertexes
int isParent(Vertex *parent, Vertex index)
{
	return parent[index] == -index-1;
}
void setUnion(Vertex *parent, Vertex start, Vertex end)
{
	if(isParent(parent, start) ) // start is the parent
	{
		if(!isParent(parent, end)) // but end is not the parent
			end = find(parent, end) + 1; // find the parent of end
		parent[start] = end;
	}
	else // start is not the parent
	{
		start = -find(parent, start) - 1; // find the parent of start
		if(!isParent(parent, end)) // but end is not the parent
			end = find(parent, end) + 1; // find the parent of end
		parent[end] = start;
	}
}
void kruskal(BinaryHeap bh, int vertexNum)
{
	int counter;
	int set1;
	int set2;
	Vertex start;
	Vertex end;
	Vertex* parent;
	ElementType singleEdge;	
	counter = 0;
	parent = makeEmptyVertexes(vertexNum);
	while(counter < vertexNum - 1)
	{
		singleEdge = deleteMin(bh);
		start = singleEdge.start;
		end = singleEdge.end;
		set1 = find(parent, start);
		set2 = find(parent, end);// find the set of vertex start and end
		if(set1 != set2)
		{
			setUnion(parent, start, end);
			counter++;
			printf("\n\t weight(v%d,v%d) = %d", singleEdge.start+1, singleEdge.end+1, singleEdge.weight);
		}
	}
	printParent(parent, vertexNum);
	printf("\n\n\t");
}
int main()
{
	BinaryHeap bh;
	ElementTypePtr temp;
	int vertexNum;
	int size = 7;
	int capacity;
	int i;
	int j;	
	int adjTable[7][7] =
	{
		{0, 2, 4, 1, 0, 0, 0},
		{2, 0, 0, 3, 10, 0, 0},
		{4, 0, 0, 2, 0, 5, 0},
		{1, 3, 2, 0, 7, 8, 4},
		{0, 10, 0, 7, 0, 0, 6},
		{0, 0, 5, 8, 0, 0, 1},
		{0, 0, 0, 4, 6, 1, 0},
	};	
	vertexNum = 7;
	capacity = vertexNum * vertexNum;
	bh = initBinaryHeap(capacity);
	temp = makeEmptyElement();
	printf("\n\n\t ====== test for kruskal alg building minimum spanning tree ======\n");
	//building binary heap with edge including 2 vertexs and its weight
	for(i = 0; i < size; i++)
	{
	 	for(j = i+1; j < size; j++)
			if(adjTable[i][j])
			{
				temp->start = i;
				temp->end = j;
				temp->weight = adjTable[i][j];
				insertHeap(temp, bh); // insertAdj the adjoining table over
			}
	}
	kruskal(bh, vertexNum);
	return 0;
} 
// allocate memory for the array with size
ElementTypePtr *makeEmptyArray(int size)
{
	ElementTypePtr *array;
	int i;
	array = (ElementTypePtr*)malloc(size * sizeof(ElementTypePtr));
	if(!array)
	{
		Error("out of space, from func makeEmptyArray");
		return NULL;
	}
	for(i=0; i<size; i++)
		array[i] = makeEmptyElement();	 
	return array;
}
// allocate memory for the single element
ElementTypePtr makeEmptyElement()
{
	ElementTypePtr temp;
	temp = (ElementTypePtr)malloc(sizeof(ElementType));
	if(!temp)
	{
		Error("out of space, from func makeEmptyElement!");
		return NULL;
	}
	return temp;
}
2.3)printing results:
最小生成树——Kruskal(克鲁斯卡尔)算法的更多相关文章
- 最小生成树之Kruskal(克鲁斯卡尔)算法
		学习最小生成树算法之前我们先来了解下下面这些概念: 树(Tree):如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的所 ... 
- ACM第四站————最小生成树(克鲁斯卡尔算法)
		都是生成最小生成树,库鲁斯卡尔算法与普里姆算法的不同之处在于——库鲁斯卡尔算法的思想是以边为主,找权值最小的边生成最小生成树. 主要在于构建边集数组,然后不断寻找最小的边. 同样的题目:最小生成树 题 ... 
- JS实现最小生成树之克鲁斯卡尔(Kruskal)算法
		克鲁斯卡尔算法打印最小生成树: 构造出所有边的集合 edges,从小到大,依次选出筛选边打印,遇到闭环(形成回路)时跳过. JS代码: //定义邻接矩阵 let Arr2 = [ [0, 10, 65 ... 
- 贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal's algorithm)
		克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个.这里面充分体现了贪心算法的精髓.大致的流程能够用一个图来表示.这里的图的选择借用了Wikiped ... 
- 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal's algorithm)
		克鲁斯卡尔算法(Kruskal's algorithm)它既是古典最低的一个简单的了解生成树算法. 这充分反映了这一点贪心算法的精髓.该方法可以通常的图被表示.图选择这里借用Wikipedia在.非常 ... 
- 经典问题----最小生成树(kruskal克鲁斯卡尔贪心算法)
		题目简述:假如有一个无向连通图,有n个顶点,有许多(带有权值即长度)边,让你用在其中选n-1条边把这n个顶点连起来,不漏掉任何一个点,然后这n-1条边的权值总和最小,就是最小生成树了,注意,不可绕成圈 ... 
- 最小生成树之克鲁斯卡尔(kruskal)算法
		#include <iostream> #include <string> using namespace std; typedef struct MGraph{ string ... 
- 最小生成树之克鲁斯卡尔(Kruskal)算法
		学习最小生成树算法之前我们先来了解下 下面这些概念: 树(Tree):如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的 ... 
- 最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)
		普里姆算法(Prim算法) #include<bits/stdc++.h> using namespace std; #define MAXVEX 100 #define INF 6553 ... 
- 最小生成树--克鲁斯卡尔算法(Kruskal)
		按照惯例,接下来是本篇目录: $1 什么是最小生成树? $2 什么是克鲁斯卡尔算法? $3 克鲁斯卡尔算法的例题 摘要:本片讲的是最小生成树中的玄学算法--克鲁斯卡尔算法,然后就没有然后了. $1 什 ... 
随机推荐
- java File和Byte[]数组 相互转换
			public class Test { public static void main(String[] args){ String filePath = "E:\\softoon\\wor ... 
- 分享最新申请IDP账号的过程,包含duns申请的分享(2013年6月)
			5月份接到公司要申请开发者账号的任务,就一直在各个论坛找申请的流程,但都是一些09年10年的比较旧的流程,现在都已经不适用了,好不容易找到2012年分享的流程吧,才发现申请过程中少了DUNS编码的步骤 ... 
- [给自己扫盲]Node.js 究竟是什么?
			Node.js 究竟是什么? 一个 “编码就绪” 服务器 Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编写能 ... 
- mysql系列-安装及服务启动
			一.window下的安装 详细见官网 https://dev.mysql.com/doc/refman/5.7/en/windows-installation.html 以 MySQL 5.1 免安装 ... 
- RTU模式与ASCII模式有什么不同
			所有设备必须必须实现 RTU 模式.ASCII 传输模式是选项,即默认设置必须为 RTU 模式. 当设备使用RTU (Remote Terminal Unit) 模式在 Modbus 串行链路通信, ... 
- Windows路由表配置:双网卡路由分流
			一.windows 路由表解释 route print - ====================================================================== ... 
- VUE的进阶 标签属性数据绑定和拼接
			在vue官网把文档扫了一遍后,就开始写网站项目了,没有设计,就百度里找了一个h5的助赢软件的网站把他copy下来,想想有点坏了,接着把内容改改吧.首先开始做一个列表展示vue实例好后,给对象添加默认数 ... 
- JS -- 一篇文章掌握RequireJS常用知识
			本文采取循序渐进的方式,从理论到实践,从RequireJS官方API文档中,总结出在使用RequireJS过程中最常用的一些用法,并对文档中不够清晰具体的内容,加以例证和分析,分享给大家供大家参考,具 ... 
- office2010使用mathtype时,出现未找到MathPage.WLL解决方案--亲测有用
			安装mathtype时,出现如下错误: 解决方案: 参考此网址中的内容:http://www.mathtype.cn/wenti/word-jianrong.html 首先需要找到在Word加载的两个 ... 
- Java反射学习总结五(Annotation(注解)-基础篇)
			Annotation(注解)简单介绍: 注解大家印象最深刻的可能就是JUnit做单元測试,和各种框架里的使用了. 本文主要简介一下注解的用法,下篇文章再深入的研究. annotation并不直接影响代 ... 
