#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;
template<class K,class V>
struct HashTablesNode
{
K _key;
V _value;
struct HashTablesNode* next;
HashTablesNode(const K& key, const V& value)
: _key(key)
, _value(value)
, next(NULL)
{}
};
static size_t BKDRHash(const char * str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
//为string定制的仿函数
struct __HashFuncS
{
size_t operator()(const string& str)
{
return BKDRHash(str.c_str());
}
};
//对 int型
struct __HashFuncInt
{
size_t operator()(const int& key)
{
return key;
}
};
template<class K, class V, class H = __HashFuncInt >
class HashTables
{
typedef HashTablesNode<K,V> Node;
public:
HashTables()
{}
HashTables(size_t _size)
{
_table.resize(GetNextCapacity(size));
}
void print()
{
for (size_t i = 0; i < _table.size();++i)
{
printf("_hashtable[%d]->", i);
Node* pcur = _table[i];
while (pcur)
{
cout << pcur->_key << " :"<<pcur->_value<<" ";
pcur = pcur->next;
}
cout << endl;
}
}
bool Find(const K& key)
{
int index = _HashFunc(key,_table.size());
Node* pcur = _table[index];
while (pcur)
{
if (pcur->_key == key)
return true;
pcur = pcur->next;
}
return false;
}
bool Insert(const K& key, const V& value)
{
CheckCapacity();
if (Find(key))
return false;
int index = _HashFunc(key,_table.size());
Node* tmp = new Node(key, value);
tmp->next = _table[index];
_table[index] = tmp;
size++;
return true;
}
bool Remove(const K& key)
{
size_t index = _HashFunc(key, _table.size());
Node* pcur = _table[index];
if (pcur->_key == key)
_table[index] = pcur->next;
else
{
while (pcur)
{
Node* prev = pcur;
pcur = pcur->next;
if (pcur->_key == key)
{
prev->next = pcur->next;
}
}
if (pcur == NULL)
return false;
}
delete pcur;
size--;
return true;
}
private:
size_t GetNextCapacity(size_t size)
{
const int _PrimeSize = 28;
static const unsigned long _PrimeList[_PrimeSize] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul,
786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul,
25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul,
805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for (size_t i = 0; i < _PrimeSize; ++i)
{
if (size < _PrimeList[i])
return _PrimeList[i];
}
return _PrimeList[_PrimeSize];
}
size_t _HashFunc(const K& key, size_t size)//除留余数法
{
return H()(key) % size;
}
void CheckCapacity()
{
if (size == _table.size())//负载因子达到1.0
{
//_table.resize(size * 2 + 3);
vector<Node*> newtable;
newtable.resize(GetNextCapacity(size));
for (size_t i = 0; i < _table.size(); ++i)
{
Node* pcur = _table[i];
while (pcur)
{
Node* pnext = pcur->next;
//
int index = _HashFunc(pcur->_key, newtable.size());
pcur->next = newtable[index];
newtable[index] = pcur;
_table[i] = pnext;
pcur = _table[i];
}
}
swap(newtable, _table);
}
}
private:
vector<Node*> _table;
size_t size;
};
用了素数表来配合除留余数法来减少冲突,用了哈希桶来处理哈希碰撞;
哈希表每个元素都是一个指针,指向一个单链表的第一个节点。插入数据就是往对应的key下面挂节点,查找的时候直接定位到对应的哈希表的位置,然后遍历单链表查找。对于这个单链表也可以其他方式来组织,如红黑树,让哈希表里面保存红黑树的根节点指针即可;
哈希桶的做法,相对于二次探测和线性探测,减少了前面数据对本次插入数据的影响。时间复杂度实际上通过优化可以达到0(1);缺点是比较浪费空间;