C++简单实现hash table

哈希表(Hash Table,又称为散列表)是根据关键字(key)来直接访问在内存存储位置的一种数据结构。与循”值”访问对应的访问方式是循”址”访问,如数组、向量vector等。

哈希函数(Hash Function):通过一个映射函数,将键值映射到存储位置来访问元素,这能加快查找速度。这个映射函数称哈希函数(散列函数)。

举例

电话薄查找号码:为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名X到首字母F(X)的一个函数关系),在首字母为W的表中查找“王”姓的电话号码,显然比直接查找要快得多。这里使用人名作为关键字,“取首字母”是这个例子中散列函数的函数法则F(x),存放首字母的表对应散列表。关键字和函数法则理论上可以任意确定。

碰撞(Collision):对不同的键值可能得到同一个散列地址,即k1!=k2,f(k1)==f(k2),这种现象称之为碰撞。

哈希函数构造法

散列函数的选取至关重要,若对于关键字集合中的任意值,经过散列函数映射到地址集合中的任何一个地址的概率均相等,则称之为均匀散列函数(UniformHash Function),从而减少碰撞。

常用的散列函数直接定址法、数字分析法、平方取中法、折叠法、除留余数法

除留余数法取关键字被某个不大于散列表长度m的数p除后所得的余数为散列地址,及hash(k)=k%p,p<=m。p一般取素数,否则容易发生碰撞。

处理碰撞

常用的方法有开放定址法、单独链表法、再散列等。

1)    开放定址法


关键字为{89,18,49,58,69}插入到一个散列表的情况,此时线性探测的方法去d=i,并假定去关键字除以10的余数为散列函数法则。


2) 单独链表法

将散列到同一个存储位置的所有元素保存在一个链表中。实现时,一种策略是散列表同一位置的所有碰撞结果都是用存放的,新元素被插入到表的前端还是后端完全取决于怎样方便。

C++简单实现hashTable

实现1

----------------------------------------------------------------------------------------------------------------------------------------------------

simpleHashTable.h

#pragma once
#include <iostream>
using namespace std;

typedef int KeyType;
#define NULLKEY -1

struct Entry{
	KeyType _key;
	int		_value;
	Entry(KeyType key=NULLKEY, int value=0):_key(key),_value(value){}
};

class hashTable{
public:
	hashTable();
	//hashTable(int tableSize);
	~hashTable();

	bool find(const Entry&  e);
	bool insert(const Entry& e);
	bool remove(const Entry& e);
	void clear();
	Entry& operator[](KeyType key);//重载下标操作;当找不到key对应的Entry时,插入Entry(key,0)
	int size();
	void display();

protected:
	int hashFunction(KeyType key);//将键值映射到对应地址
	void rehash();//调整hashTable大小
	bool find(const KeyType& k);//按键值查找
	int nextPrime();//p(n) = n^2 - n + 41, n<41, p<1681

private:
	Entry *_pTable;
	int _pos;//当前访问元素的位置
	int _size;
	int _capacity;
	int primeIndex;
};


simpleHashTable.cpp

#include "simpleHashTable.h"

hashTable::hashTable()
{
	_capacity = 3;//初始化hashTable容量为3,便于观察rehash过程
	_pTable = new Entry[_capacity];
	_size = 0;
	primeIndex = 1;
}

//hashTable::hashTable(int tableSize)
//{
//
//}

hashTable::~hashTable()
{
	clear();
}

int hashTable::nextPrime()
{
	int p = std::pow(static_cast<float>(primeIndex),2) - primeIndex + 41;
	primeIndex = primeIndex << 1;
	if(primeIndex >= 41){
		cout << "Max capacity reached. exit!" << endl;
		exit(-1);
	}
	return p;
}

bool hashTable::find(const Entry&  e)
{
	return(find(e._key));
}

bool hashTable::find(const KeyType& k)
{
	_pos = hashFunction(k);
	if(_pTable[_pos]._key==NULLKEY)
		return false;
	int lastPos = _pos;
	while(_pTable[_pos]._key!=k){
		if(++_pos%_capacity == lastPos)
			return false;
	}
	return true;
}

bool hashTable::insert(const Entry& e)
{
	if((_size*1.0)/_capacity>0.75)
		rehash();//[OK]插入操作前,需要判断hash table是否需要扩容
	if(find(e))
		return false;
	_pTable[_pos] = e;
	++_size;
	return true;
}

bool hashTable::remove(const Entry& e)
{
	if(!find(e))
		return false;
	_pTable[_pos]._key = NULLKEY;
	--_size;
	//rehash();//移除操作后,需要判断hash table是否需要缩容
	return true;
}

void hashTable::clear()
{
	delete []_pTable;
	_size = _capacity = 0;
}

Entry& hashTable::operator[](KeyType key)
{
	if(!find(key))
		insert(Entry(key,0));
	return _pTable[_pos];
}

int hashTable::size()
{
	return _size;
}

int hashTable::hashFunction(KeyType key)
{
	return key%_capacity;
}

void hashTable::display()
{
	cout << "capacity = " << _capacity << ", size = " << _size << endl;
	for(int i=0; i<_capacity; i++){
		if(_pTable[i]._key!=NULLKEY)
			cout << "key=" << _pTable[i]._key << ",value=" << _pTable[i]._value << endl;
	}
}


void hashTable::rehash()
{	
	cout << "begin rehash..." << endl;
	Entry *p = new Entry[_size];//用来暂存原哈希表
	for(int i=0; i<_capacity; i++){//i<_size不对;元素散列在容量为_capacity的hashTable中
		if(_pTable[i]._key != NULLKEY)
			*(p+i) = _pTable[i];
	}
	delete []_pTable;
	int lastSize = _size;
	_size = 0;	

	_capacity = nextPrime();
	_pTable = new Entry[_capacity];
	for(int i=0; i<lastSize; i++)
		insert(*(p+i));
	delete []p;		
}
测试代码 testbench.cpp

#include <iostream>
#include "simpleHashTable.h"

using namespace std;

int main()
{
	hashTable *pTable = new hashTable;
	cout << "insert Entry(1,11)..." << endl;
	pTable->insert(Entry(1,11));
	pTable->display();
	cout << "insert Entry(2,22)..." << endl;
	pTable->insert(Entry(2,22));
	pTable->display();
	cout << "insert Entry(3,33)..." << endl;
	pTable->insert(Entry(3,33));
	pTable->display();
	cout << "insert Entry(4,44)..." << endl;
	//pTable->insert(Entry(4,44));
	(*pTable)[4]._value = 44;//下标操作,返回值充当左值
	pTable->display();

	cout << endl << "delete Entry(1,11)..." << endl;
	pTable->remove(Entry(1,11));
	pTable->display();
	cout << "delete Entry(2,22)..." << endl;
	pTable->remove(Entry(2,22));
	pTable->display();
	cout << "delete Entry(3,33)..." << endl;
	pTable->remove(Entry(3,33));
	pTable->display();

	cout << "delete Entry(3,33)..." << endl;
	pTable->remove(Entry(3,33));
	pTable->display();
	
	delete pTable;
	getchar();
	return 0;
}

----------------------------------------------------------------------------------------------------------------------------------------------------

实现2

simpleHashTable_v2.h

#pragma once
#include <iostream>
using namespace std;

typedef int KeyType;
struct Entry
{
	KeyType _key;
	int _value;
	Entry(KeyType k=0, int v=0):_key(k),_value(v){}
};

class hashTable
{
public:
	hashTable();
	~hashTable();

	void clear();//清除hashTable所有元素
	bool find(const Entry& e);//true:成功;false:失败
	bool insert(const Entry& e);//true:成功;false:失败
	bool remove(const Entry& e);//true:成功;false:失败
	Entry* operator[](const KeyType& key);//若不存在键值为key的元素,则返回NULL
	void display();//逐条打印hashTable信息
protected:
	int hashFunction(const KeyType& key);//示例采用简单的映射关系,返回(key%_capacity)
	bool find(const KeyType& key);//按键值访问
	void rehash();//当载荷因子(_size/_capacity)大于0.75时,重新散列
	int nextPrime();//p=n^2-n+41, n<41
	int _primeN;//代表上面公式的n,在rehash中会逐渐变大
private:
	Entry **_pTable;
	int _size;//哈希表中元素的个数
	int _capacity;//哈希表的容量
	int _pos;//当前访问的元素位置
	
};
simpleHashTable_v2.cpp

#include "simpleHashTable_v2.h"

hashTable::hashTable()
{
	_size = 0;
	_capacity = 3;//初始化hashtable容积为3
	_pTable = new Entry*[_capacity];
	for(int i=0; i<_capacity; i++)
		_pTable[i] = NULL;
	_primeN = 1;
}

hashTable::~hashTable()
{
	clear();
}

void hashTable::clear()
{
	delete []_pTable;
	_size = 0;
	_capacity = 0;
}

int hashTable::nextPrime()
{
	if(_primeN > 41)
		exit(-1);
	int prime = std::pow(_primeN*1.0,2) - _primeN + 41;
	_primeN = _primeN << 1;
	return prime;
}

int hashTable::hashFunction(const KeyType& key)//一种简单的哈希函数
{
	return key%_capacity;
}

bool hashTable::find(const Entry& e)
{
	return find(e._key);
}

bool hashTable::find(const KeyType& key)
{
	_pos = hashFunction(key);
	if(_pTable[_pos]==NULL)
		return false;
	for(int i=0; i<=_capacity; i++){
		_pos = (_pos+i)%_capacity;//采用开放地址放解决冲突,尝试_pos, _pos+1, _pos+2...
		if(_pTable[_pos]!=NULL && _pTable[_pos]->_key==key){
			return true;
		}
	}
		return false;
}

bool hashTable::insert(const Entry& e)
{
	if((_size*1.0)/_capacity > 0.75)//装载因子大于0.75,重新散列
		rehash();
	if(find(e))//如果e已经存在hashTable中,返回false
		return false;
	_pTable[_pos] = new Entry(e);//当前位置插入e
	_size++;//hashTable规模增1
	return true;
}

bool hashTable::remove(const Entry& e)
{
	if(find(e)){//e存在hashTable中,执行删除操作,hashTable规模减1
		delete _pTable[_pos];//回收内存
		_pTable[_pos]=NULL;//置空指针
		_size--;//hashTable规模减1
		return true;
	}
	return false;//否则返回false
}

Entry* hashTable::operator[](const KeyType& key)
{
	if(find(key))
		return _pTable[_pos];
	else
		return NULL;
}

void hashTable::rehash()
{//重新散列
	Entry **pTableOld = new Entry*[_size];
	for(int i=0; i<_capacity; i++)//遍历hashTable,备份所有有效的元素
		if(_pTable[i] != NULL)
			pTableOld[i] = new Entry(*_pTable[i]);

	delete []_pTable;
	_capacity = nextPrime();//hashTable增容
	_pTable = new Entry*[_capacity];
	for(int i=0; i<_capacity; i++)
		_pTable[i] = NULL;
	int sizeOld = _size;
	_size = 0;
	for(int i=0; i<sizeOld; i++)//将之前所有的元素逐一插入
		insert(*pTableOld[i]);
}

void hashTable::display()
{
	cout << "capacity=" << _capacity <<",size="<<_size<<endl;
	for(int i=0; i<_capacity; i++)
		if(_pTable[i]!=NULL)
			cout<<"key="<<_pTable[i]->_key<<",value="<<_pTable[i]->_value<<endl;
}

测试代码testbench.cpp

#include <iostream>
#include <string>
#include <vector>
#include "simpleHashTable_v2.h"

using namespace std;

int main()
{
	hashTable *pTable = new hashTable;
	cout << "insert Entry(1,11)..." << endl;
	pTable->insert(Entry(1,11));
	pTable->display();
	cout << "insert Entry(2,22)..." << endl;
	pTable->insert(Entry(2,22));
	pTable->display();
	cout << "insert Entry(3,33)..." << endl;
	pTable->insert(Entry(3,33));
	pTable->display();
	cout << "insert Entry(4,44)..." << endl;
	pTable->insert(Entry(4,44));
	(*pTable)[1]->_value = 101;//下标操作,返回值充当左值
	pTable->display();

	cout << endl << "delete Entry(1,11)..." << endl;
	pTable->remove(Entry(1,11));
	pTable->display();
	cout << "delete Entry(2,22)..." << endl;
	pTable->remove(Entry(2,22));
	pTable->display();
	cout << "delete Entry(3,33)..." << endl;
	pTable->remove(Entry(3,33));
	pTable->display();

	cout << "delete Entry(3,33)..." << endl;
	pTable->remove(Entry(3,33));
	pTable->display();
	
	delete pTable;
	getchar();
	return 0;
}

补充:

Stl中哈希表hash_map介绍(待续)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值