哈希表(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
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;
};
#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(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
测试代码 testbench.cpp
#include
#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;
}
----------------------------------------------------------------------------------------------------------------------------------------------------
实现2simpleHashTable_v2.h
#pragma once
#include
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_key<<",value="<<_pTable[i]->_value<
#include
#include
#include
#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介绍(待续)