用c++封装一个Hash Table,并与STL map 进行操作性能上的比较

问题描述:

1.在计算机科学中,hash table 是一种常用的数据结构,它本质上是一种关联容器,它映射一个key 到value。它通过一个hash function把key映射成一个整数值,这个整数值对应存放value值的容器的下标。

2.它主要支持三种操作:插入key,value对(insert),查询(search)给定key, 删除key, value对(delete);

3.它三种操作的平均时间复杂度为O(1),最糟糕情况下的时间复杂度为O(n);

4.hash table要处理核心问题是选择好的hash function,这能使得key映射出的index足够分散均匀,尽量减少碰撞(不同的key映射到同一个index),为了消除碰撞产生,一般常用几种方法:Separate chaining, Linear probing,Quadratic probing, Rehash, double Hashing,详细细节请参看维基百科;

5.本文给出利用Rehash的策略来消除碰撞;

6.本文设计测试用例,比较了hashtable 和STL map 的操作性能。发现执行相同的操作,hashtable 所消耗时间为STL map 九分之一;

程序代码:

#ifndef _HASH_TABLE_H_
#define _HASH_TABLE_H_

#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <assert.h>
#include <string>

#include "windows.h"

/*
* Compare function
*
*/
static bool StrCompare( const char* first, const char* second )
{
	size_t firstLen = strlen(first);
	size_t secondLen = strlen(second);

	return firstLen == secondLen && !strcmp( first, second );
}

/*
* Hash function
*
*/
static unsigned int ImplHashFunc( const char* buf, int len )
{
	unsigned int hash = 5381;

	while(len--)
	{
		hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
	}

	return hash;
}

/*
* Hash function
*
*/
static unsigned int ImplCaseHashFunc( const unsigned char* buf, int len )
{
	unsigned int hash = 5381;

	while(len--)
	{
		hash = ((hash << 5) + hash) + tolower(*buf++); /* hash * 33 + c */
	}

	return hash;
}

/*
* Hash function
*
*/
static unsigned int ImplHashFuncSimple( unsigned int key )
{
	key += ~(key << 15);
	key ^=  (key >> 10);
	key +=  (key << 3);
	key ^=  (key >> 6);
	key += ~(key << 11);
	key ^=  (key >> 16);
	return key;
}

/*
* encapsulate the hash table
* advantage:
*          good performance;
*          terse interface to make more easy for outstanding and to employ
*
*/
template<class T>
class HashTable
{
public:
	typedef unsigned int (*HashFunctor)( const char* key, int len);
	typedef bool (*KeyCompare)( const char* keyFirst, const char* keySecond );

	static const int INIT_TABLE_SIZE = 689981;	

	typedef struct tagEntryNode
	{
		char*  key;
		T      value;
		tagEntryNode* next;

		tagEntryNode():key(0), value(), next(0)
		{

		}

		tagEntryNode( const char* _key, const T& val ):
			value(val), next(0)
		{
			size_t len = strlen(_key) + 1;
			key = new char[len];
			strncpy( key, _key, len - 1);
			key[len - 1] = ‘\0‘;
		}

		~tagEntryNode()
		{
			delete [] key;
			key = 0;
		}

	}EntryNode, *pEntryNode;

	typedef struct tagHashNode
	{
		EntryNode** table;
		size_t      used;
		size_t      size;
		size_t      sizeMask;

		tagHashNode():table(0), used(0), size(0),sizeMask(0)
		{

		}

		tagHashNode( size_t _size ):table(0), used(0), size(_size),
			                        sizeMask(0)
		{
			Init( _size );

		}

		~tagHashNode()
		{
		}

		void Init( size_t _size )
		{
			size = _size;
			sizeMask = size - 1;
			table = new EntryNode*[size];
			memset( table, 0x00, sizeof(EntryNode*)*size );
		}

	}HashNode, *pHashNode;

	/*
	*
	*
	*/
	HashTable( HashFunctor functor = ImplHashFunc, KeyCompare cmpFunctor = StrCompare ):m_hashFunctor(functor),
		         m_keyCmpFunctor(cmpFunctor),
		         m_hashTable(new HashNode), m_resizeRatio(2)
	{
		Init( INIT_TABLE_SIZE );
	}

	/*
	*
	*
	*/
	~HashTable()
	{
		Clear();
	}

	/*
	* Clear all node and entity
	*
	*/
	void Clear()
	{
		Clear( m_hashTable );
	}

	/*
	* Inset the pair of key and value
	*
	*/
	bool Insert( const char* key, const T& value )
	{
		Rehash();

		return Insert( m_hashTable, key, value );
	}

	/*
	* Retrieve the pointer of value for given key
	*
	*/
	T* Find( const char* key )
	{
		unsigned int hash = m_hashFunctor(key, strlen(key));
		unsigned int idx = hash % m_hashTable->size;

		EntryNode* entry = m_hashTable->table[idx];
		while( entry )
		{
			if( m_keyCmpFunctor( entry->key, key) )
			{
				return &entry->value;
			}

			entry = entry->next;
		}

		return NULL;
	}

	/*
	* Delete hashEntry for given key
	*
	*/
	void Delete( const char* key )
	{
		unsigned int hash = m_hashFunctor(key, strlen(key));
		unsigned int idx = hash % m_hashTable->size;

		EntryNode* entry = m_hashTable->table[idx];
		EntryNode* preEntry = 0;
		while( entry )
		{
			if( m_keyCmpFunctor( entry->key, key ) )
			{
				if( preEntry )
				{
					preEntry->next = entry->next;
				}
				else
				{
					m_hashTable->table[idx] = entry->next;
				}

				delete entry;
				entry = 0;
				m_hashTable->used--;
				return;
			}
			else
			{
				preEntry = entry;
				entry  = entry->next;
			}

		}
	}

protected:

	/*
	* Fink the index of corresponding of key value in the table
	*
	*/
	int FindKeyIndex( pHashNode hashNode, const char* key )
	{
		unsigned int hash = m_hashFunctor( key, strlen( key ) );
		unsigned int idx = hash % hashNode->size;

		EntryNode* entry = hashNode->table[idx];
		if( 0 == entry )
			hashNode->used++;

		while( entry )
		{
			if( m_keyCmpFunctor( entry->key, key) )
			{
				return -1;
			}

			entry = entry->next;
		}

		return idx;
	}

	/*
	* Implement insert operation
	*
	*/
	bool Insert( pHashNode hashNode, const char* key, const T& value )
	{
		int idx = FindKeyIndex( hashNode, key );
		if( idx != -1 )
		{
			EntryNode* newNode = new EntryNode( key, value );
			newNode->next = hashNode->table[idx];
			hashNode->table[idx] = newNode;

			return true;
		}

		return false;
	}

	/*
	* Rehash double store memory to make more root then remake insert operation
	* very important
	*/
	void Rehash()
	{
		if( m_hashTable->used >= m_hashTable->size ||
			  (m_hashTable->used > 0 && (m_hashTable->size / m_hashTable->used) < m_resizeRatio ) )
		{
			size_t newSize = NextPrime( m_hashTable->size * 2 );
			pHashNode newHashNode = new HashNode( newSize );

			for( size_t i = 0; i < m_hashTable->size; i++ )
			{
				pEntryNode entryNode = m_hashTable->table[i];
				while( entryNode )
				{
					Insert( newHashNode, entryNode->key, entryNode->value );
					entryNode = entryNode->next;
				}

			}

			Clear( m_hashTable );
			m_hashTable = newHashNode;

		}
	}

	/*
	* Implement clear operation
	*
	*/
	void Clear( pHashNode hashNode )
	{
		for( size_t i = 0; i < m_hashTable->size; i++ )
		{
			pEntryNode entryNode = m_hashTable->table[i];
			while( entryNode )
			{
				pEntryNode next = entryNode->next;
				delete entryNode;
				entryNode = next;
			}

		}

		delete [] m_hashTable->table;
	}

	/*
	* Initialization
	*
	*/
	void Init( size_t tableSize )
	{
		m_hashTable->Init( tableSize );
	}

	/*
	* Helper function
	* check prime
	*/
	bool IsPrime( size_t x)
	{
		for( std::size_t i = 3; true; i += 2 )
		{
			std::size_t q = x / i;
			if( q < i )
				return true;

			if( x == q * i )
				return false;
		}
		return true;
	}

	/*
	* Find next prime for given x
	*
	*/
	size_t	NextPrime( size_t x )
	{
		if( x <= 2 )
			return 2;

		if(!(x & 1))
			++x;

		for(; !IsPrime(x); x += 2 );

		return x;
	}

protected:

	HashFunctor m_hashFunctor;

	KeyCompare  m_keyCmpFunctor;

	pHashNode   m_hashTable;

	size_t      m_resizeRatio;

};

/*
* Test hash table
*
*/
void TestHashTable()
{
	unsigned long start = GetTickCount();

	HashTable<int> hashTable;
	const int Len = 500000;
	for( int i = 0; i < Len; i++ )
	{
		char key[16] = {0};
		sprintf(key, "%s_%d", "china", i );

		assert(hashTable.Insert( key, i ));
	}

	for( int i = 0; i < Len; i++ )
	{
		char key[16] = {0};
		sprintf(key, "%s_%d", "china", i );

		if( i > 0 && !(i % 50) )
		{
			hashTable.Delete( key );
			assert( !hashTable.Find( key ) );
		}
		else
		{
			assert(i == *hashTable.Find( key));
		}

	}

	unsigned long interval = GetTickCount() - start;
	printf(" hash table consume time is %d \n", interval );
}

/*
* Test STL map
*
*/
void TestHTSTLMap()
{
	unsigned long start = GetTickCount();

	std::map<std::string, int > strMap;
	const int Len = 500000;
	for( int i = 0; i < Len; i++ )
	{
		char key[16] = {0};
		sprintf(key, "%s_%d", "china", i );
		std::string keyStr(key);

		strMap.insert( std::make_pair(keyStr, i )) ;
	}

    std::map<std::string, int >::iterator iter = strMap.begin();
	for( int i = 0; i < Len; i++ )
	{
		char key[16] = {0};
		sprintf(key, "%s_%d", "china", i );
		std::string keyStr(key);

		if( i > 0 && !(i % 50) )
		{
			strMap.erase( key );
			assert( strMap.find( key ) == strMap.end() );
		}
		else
		{
			iter = strMap.find( keyStr );
			assert( iter->second == i );
		}

	}

	unsigned long interval = GetTickCount() - start;
	printf(" STL map consume time is %d \n", interval );
}

/*
* Test suite and compare performance
*
*/
void TestSuiteHashTable()
{
	TestHashTable();
	TestHTSTLMap();

}

#endif 

compile and run in visual studio 2005

用c++封装一个Hash Table,并与STL map 进行操作性能上的比较

时间: 2024-12-14 18:44:48

用c++封装一个Hash Table,并与STL map 进行操作性能上的比较的相关文章

扩展封装暴雪哈希算法(blizard hash algorithm),并与STL map进行操作性能上的比较

问题描述: 1.blizard hash algorithm 是众所周知的算法,关于它极小的碰撞概率和实现的简洁性一直为热爱技术的朋友津津乐道: 2.blizard hash algorithm 有个致命的问题就是它的实现受制于一个固定的(预先开辟的buffer)的限制,暴雪给出的是1024,也即当hash table 的填充的元素(key value pair)查过1024时,就没办法再往里面进行key value 对填充,这极大的限制了它的使用.在实现的应用,我们经常会向hash table

[CareerCup] 13.2 Compare Hash Table and STL Map 比较哈希表和Map

13.2 Compare and contrast a hash table and an STL map. How is a hash table implemented? If the number of inputs is small, which data structure options can be used instead of a hash table? 这道题让我们比较哈希表和STL中的map数据结构,在遇到这道题之前,我一直以为map就是c++中的哈希表呢,原来是不同的啊-

哈希表 hash table

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数. 首先问题规模确定,例如5台服务器怎么把数据散落在5台上面呢,就用到了hash算法

5105 pa2 Distributed Hash Table based on Chord

1 Design document 1.1 System overview We implemented a Book Finder System using a distributed hash table (DHT) based on the Chord protocol. Using this system, the client can set and get the book’s information (title and genre for simplicity) using th

[译]C语言实现一个简易的Hash table(2)

上一章,简单介绍了Hash Table,并提出了本教程中要实现的几个Hash Table的方法,有search(a, k).insert(a, k, v)和delete(a, k),本章将介绍Hash table使用的数据结构. Hash table数据结构 hash表中存储的每一项key-value的数据结构: // hash_table.h typedef struct { char* key; char* value; } ht_item; 我们的hash表中保存着一个指向每一项的指针数组

[译]C语言实现一个简易的Hash table(3)

上一章,我们讲了hash表的数据结构,并简单实现了hash表的初始化与删除操作,这一章我们会讲解Hash函数和实现算法,并手动实现一个Hash函数. Hash函数 本教程中我们实现的Hash函数将会实现如下操作: 输入一个字符串,然后返回一个0到m(Hash表的大小)的数字 为一组平常的输入返回均匀的bucket索引.如果Hash函数不是均匀分布的,就会将多个记录插入到相同的bucket中,这就回提高冲突的几率,而这个冲突就会影响到我们的Hash表的效率. Hash算法 我们将会设计一个普通的字

[译]C语言实现一个简易的Hash table(4)

上一章我们解释了Hash table中最重要的hash函数,并用伪代码和C语言实现了一个我们自己的hash函数,hash函数中碰撞是无法避免的,当发生碰撞时我们改如何有效的处理呢?这章我们就来讲解下. 处理碰撞 hash函数中将无限大的输入映射到有限的输出中,当不同的输入映射到相同的输出时,就会发生碰撞,每个的hash表都会采用不同的方法来处理碰撞. 我们的哈希表将使用一种称为开放地址的双重哈希的技术来处理冲突.双重哈希使用两个散列函数来计算在发生碰撞后存储记录的索引. 双重哈希 当i发生碰撞后

[译]C语言实现一个简易的Hash table(7)

上一章我们讲了如何根据需要动态设置hash表的大小,在第四章中,我们使用了双重哈希来解决hash表的碰撞,其实解决方法有很多,这一章我们来介绍下其他方法. 本章将介绍两种解决hash表碰撞的方法: 拉链法 开放地址法 拉链法 使用拉链法,每一个bucket都会包含一个链接表,当发生碰撞时,就会将该记录插入在该位置的链接表后面,步骤如下: 插入时:通过hash函数获取到要插入的位置,如果该位置是空的,就直接插入,如果该位置不是空的,就插入在链接表的后面 搜索时:通过hash函数获取到key对应的位

stl源码分析之hash table

本文主要分析g++ stl中哈希表的实现方法.stl中,除了以红黑树为底层存储结构的map和set,还有用哈希表实现的hash_map和hash_set.map和set的查询时间是对数级的,而hash_map和hash_set更快,可以达到常数级,不过哈希表需要更多内存空间,属于以空间换时间的用法,而且选择一个好的哈希函数也不那么容易. 一. 哈希表基本概念 哈希表,又名散列表,是根据关键字直接访问内存的数据结构.通过哈希函数,将键值映射转换成数组中的位置,就可以在O(1)的时间内访问到数据.举