并查集类的c++封装,比较union_find algorithm四种实现方法之间的性能差别

问题描述:

计算机科学中,并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint
Sets)的合并及查询问题。有一个联合-查找算法union-find algorithm)定义了两个操作用于此数据结构:

Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集;

Union:将两个子集合并成同一个集合;

实现并查集的关键是实现union-find algorithm, 本文根据常用的四种算法,实现了这个类,具体算法实现请参看维基百科;

制造测试数据集,测试几种方法之间性能的指标;

程序代码:

#ifndef _DISJOINT_SET_H_
#define _DISJOINT_SET_H_

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <math.h>

#include "windows.h"

enum DISJOINTWAY
{
	COMMON_WAY,
	COMPREE_WAY,
	WEIGHT_WAY,
	WEIGHT_COMPRESS_WAY
};

/*
* encapsulate the class of disjoint set
*
*/

#define MAXDISJOINTSET 0xffffff
class DisjointSet
{
public:
	DisjointSet( int maxSize = MAXDISJOINTSET ):m_item(0), m_size(maxSize)
	{
		m_item = new int[maxSize];
		for( int i = 0; i < m_size; i++ )
		{
			m_item[i] = i;
		}

		m_path = new int[maxSize];
		memset( m_path, 1, sizeof(int)*maxSize );
	}

	~DisjointSet()
	{
		Clear();
	}

	/*
	* find interface
	*
	*/
	int Find( DISJOINTWAY way, int input )
	{
		assert( input <  m_size );
		switch( way )
		{
		case COMMON_WAY:
			return ImplFindFirst( input );
		case COMPREE_WAY:
			return ImplFindSecond( input );
		case WEIGHT_WAY:
			return ImplFindWeight( input );
		case WEIGHT_COMPRESS_WAY:
			return ImplFindWeightCompree( input );
		default:
			return -1;
		}
	}

	/*
	* make union
	*
	*/
	void Union( DISJOINTWAY way, int first, int second )
	{
		assert( first < m_size && second < m_size );
		switch( way )
		{
		case COMMON_WAY:
			ImplUnionFirst( first, second );
			break;
		case COMPREE_WAY:
			ImplUnionSecond( first, second );
			break;
		case WEIGHT_WAY:
			ImplUnionWeighted( first, second );
			break;
		case WEIGHT_COMPRESS_WAY:
			ImplUnionCompree( first, second );
			break;
		default:
			break;
		}

	}

	/*
	*
	*
	*/
	void Clear()
	{
		delete [] m_item;
		m_item = 0;

		delete [] m_path;
		m_path = 0;

		m_size = 0;
	}

protected:

	int ImplFindFirst( int input )
	{
		assert( input < m_size  );
		return m_item[input];
	}

	int ImplFindSecond( int input )
	{
		int i = input;
		for( ; i != m_item[i]; i = m_item[i] );

		return i;
	}

	int ImplFindWeight( int input )
	{
		int i = input;
		for( ; i != m_item[i]; i = m_item[i] );

		return i;

	}

	int ImplFindWeightCompree( int input )
	{
		int i = input;
		for( ; i != m_item[i]; i = m_item[i] )
			m_item[i] = m_item[m_item[i]];

		return i;
	}	

	/*
	*
	*
	*/
	void ImplUnionFirst( int first, int second )
	{
		int x = m_item[first];
		int y = m_item[second];

		if( x != y )
		{
			m_item[first] = y;
		}

		for( int i = 0; i < m_size; i++ )
		{
			if( x == m_item[i] )
				m_item[i] = y;
		}
	}

	/*
	*
	*
	*/
	void ImplUnionSecond( int& first, int& second )
	{
		if( first != second )
		{
			m_item[first] = second;
		}
	}

	/*
	*
	*
	*/
	void ImplUnionWeighted( int first, int second )
	{
		if( first != second )
		{
			if( m_path[first] < m_path[second] )
			{
				m_item[first] = second;
				m_path[second] += m_path[first];
			}
			else
			{
				m_item[second] = first;
				m_path[first] += m_path[second];
			}
		}
	}

	/*
	*
	*
	*/
	void ImplUnionCompree( int first, int second )
	{
		if( first != second )
		{
			if( m_path[first] < m_path[second] )
			{
				m_item[first] = second;
				m_path[second] += m_path[first];
			}
			else
			{
				m_item[second] = first;
				m_path[first] += m_path[second];
			}
		}

	}

protected:

	int*   m_item;
	int    m_size;

	int*   m_path;

};

void TestDisjointSetSimple()
{
	DisjointSet djoint;
	int i = djoint.Find( COMMON_WAY, 1 );
	int j = djoint.Find( COMMON_WAY, 3 );
	if( i != j )
		djoint.Union( COMMON_WAY, 1, 3 );

	i = djoint.Find( COMMON_WAY, 2 );
	j = djoint.Find( COMMON_WAY, 5 );
	if( i != j )
		djoint.Union( COMMON_WAY, i, j );

	i = djoint.Find( COMMON_WAY, 2 );
	j = djoint.Find( COMMON_WAY, 6 );
	if( i != j )
		djoint.Union( COMMON_WAY, i, j );

	i = djoint.Find( COMMON_WAY, 6 );
	j = djoint.Find( COMMON_WAY, 7 );
	if( i != j )
		djoint.Union( COMMON_WAY, i, j );

	assert( djoint.Find( COMMON_WAY, 2 ) == djoint.Find( COMMON_WAY, 7 ) );

	i = djoint.Find( COMMON_WAY, 1 );
	j = djoint.Find( COMMON_WAY, 7 );
	if( i != j )
		djoint.Union( COMMON_WAY, i, j );

	assert( djoint.Find( COMMON_WAY, 3 ) == djoint.Find( COMMON_WAY, 7 ) );
}

void TestDisjointSetComplex( DISJOINTWAY way, const char* str )
{

    unsigned long start = GetTickCount();
	DisjointSet djoint;

	const int len = 1000000;
	const int base = 60000;
	int halfLen = len / 2;
	srand( time(NULL) );
	for( int i = 0; i < len; i++ )
	{
		int first = rand() % base;
		int second = rand() % base;
		if( i > halfLen )
		{
			first += base;
			second += base;
		}

		if( first != second )
		{
			first = djoint.Find( way, first );
			second = djoint.Find( way, second );
			if( first != second )
				djoint.Union( way, first, second );

			assert( djoint.Find( way, first ) == djoint.Find( way, second )  );
		}
	}

	unsigned long interval = GetTickCount() - start;
	printf(" %s way consume time is %d \n", str, interval );

}

void TestSuiteDisjointSet()
{
	TestDisjointSetSimple();

	const char* str[] = {"common", "compress", "weight", "weight compress"};
	for( int i = WEIGHT_COMPRESS_WAY; i >= 0; i--)
	{
		TestDisjointSetComplex((DISJOINTWAY)i, str[i] );
	}

}

#endif 

compile and run in visual studio 2005

下面图片是几种方法运行时间之比较,最直白方法的时间到现在还没输出,所以就没有显示:

并查集类的c++封装,比较union_find algorithm四种实现方法之间的性能差别

时间: 2024-10-27 03:56:05

并查集类的c++封装,比较union_find algorithm四种实现方法之间的性能差别的相关文章

并查集类的c++封装,比較union_find algorithm四种实现方法之间的性能区别

问题描写叙述: 在计算机科学中,并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.有一个联合-查找算法(union-find algorithm)定义了两个操作用于此数据结构: Find:确定元素属于哪一个子集.它能够被用来确定两个元素是否属于同一子集: Union:将两个子集合并成同一个集合: 实现并查集的关键是实现union-find algorithm, 本文依据经常使用的四种算法,实现了这个类,详细算法实现请參看维基百科: 制造測试

数据库的四种操纵方法——增、删、改、查

数据库的四种操纵方法——增.删.改.查 增——一种可视化增加就是在设计页面右键点开已经存在的表 进行内容的增加. 另一种是在查询页面,创建查询在代码界面进行代码添加.书写形式:insert into xxx(zzz,ccc)values('aaa','sss')——insert是插入的意思,into是进哪去,xxx代表要插入的表名,zzz,ccc表示表内的列名,values的意思是‘值’,后面的xxx和ccc是插入所内容的列名.总体的意思就是:在xxx表内的zzz列和ccc列插入aaa和sss

【cocos2d-x学习笔记】三种文字类、批处理精灵、C++的四种cast

三种显示文字的类 CCLabelTTF, CCLabelAtlas, CCLabelBMFont CCLabelTTF:使用系统字体每个字符串会生成一个纹理,显示效率比较低下.适合无变化的文字 CCLabelAtlas: 使用NodeAtlas优化渲染,适合经常变化的数字,比如分数,金钱. CCLabelBMFont: 使用CCSpriteBatchNode,很灵活,每个字符都是一个精灵,可以对每一个字符进行操作. CCLabelAtlas *lable = CClabelAtlas::crea

RBAC类在ThinkPHP中的四种使用方法

第一类:放在登陆控制器的登陆操作中 1.RBAC::authenticate(); 用于在用户表中查找表单提交的用户名的数据,实质上就是一条用户表查寻语句,=====> return M(modle)->where(array)->find();这个操作有两个参数 a.array()数组的写法及作用和表查寻数组一样,=====>array(‘字段’=>‘值’,‘字段’=>array('条件','值')); b.model就是表名,默认是配制参数 C('USER_AUTH

不相交集类(并查集)

并查集 就是只有合并和 查找操作的一种数据结构  很简单,主要判断一个元素是否在一个集合里 主要应用在最小生成树(Kruskal算法),看到图的时候会将实现代码贴上 package chapter8; /** * 类名:DisjSets 说明:实现并查集 按高度求并,路径压缩 s[i]代表i的父节点 */ public class DisjSets { public DisjSets(int numElements) { s = new int[numElements]; for (int i

*并查集的题*

POJ 1182 食物链http://acm.pku.edu.cn/JudgeOnline/problem?id=1182题目告诉有3种动物,互相吃与被吃,现在告诉你m句话,其中有真有假,叫你判断假的个数(如果前面没有与当前话冲突的,即认为其为真话)这题有几种做法,我以前的做法是每个集合(或者称为子树,说集合的编号相当于子树的根结点,一个概念)中的元素都各自分为A, B, C三类,在合并时更改根结点的种类,其他点相应更改偏移量.但这种方法公式很难推,特别是偏移量很容易计算错误.下面来介绍一种通用

并查集及应用

在信息学竞赛中,并查集是一种不可忽视的一部分内容,把最近几年的NOI和NOIP复赛题目大致浏览了一遍,发现有好几道应用并查集的题目,因此本文由浅入深的介绍并查集在编程中的巧妙应用. 什么是并查集?并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示.集就是让每个元素构成一个单元素的集合,并就是按一定顺序将属于同一组的元素所在的集合合并. 并查集的主要操作: 1.初始化:把每个点所在集合初始化为其自身: 2.查找:查找元素所在的

数据结构--并查集的原理及实现

一,并查集的介绍 并查集(Union/Find)从名字可以看出,主要涉及两种基本操作:合并和查找.这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合. 而在某次合并之后,有一种合理的需求:某两个元素是否已经处在同一个集合中了?因此就需要Find操作. 并查集是一种 不相交集合 的数据结构,设有一个动态集合S={s1,s2,s3,.....sn},每个集合通过一个代表来标识,该代表中集合中的某个元素. 比如,若某个元素 x 是否在集合 s1 中(Fi

Corporative Network_并查集

Description A very big corporation is developing its corporative network. In the beginning each of the N enterprises of the corporation, numerated from 1 to N, organized its own computing and telecommunication center. Soon, for amelioration of the se