哈希冲突
不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
哈希函数
引起哈希冲突的一个原因可能是:哈希函数设计不够合理。
哈希函数设计原则:哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间。
线性探测
从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
-
插入
通过哈希函数获取待插入元素在哈希表中的位置,如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素。 -
删除
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他
元素的搜索。因此线性探测采用标记的伪删除法来删除一个元素。 -
线性探测优点:实现非常简单
-
线性探测缺点:一旦发生哈希冲突,所有的冲突连在一起,容易产生数据“堆积”,即:不同关键码占据了可利用的空位置,使得寻找某关键码的位置需要许多次比较,导致搜索效率降低。
代码实现
- 节点定义
enum State{EMPTY,EXIST,DELETE};
template <class K,class V>
struct HashNode
{
pair<K, V> _data;
State _state = EMPTY;
};
- 结构
template <class K, class V>
class HashTable
{
public:
typedef HashNode<K, V> Node;
typedef Node* pNode;
HashTable(const int n = 10)
{
_hs.resize(n);
_size = 0;
}
bool Insert(const pair<K,V>&data);
void CheckCapacity();
pNode find(const K& key);
bool Erase(const K& key);
private:
vector<Node> _hs;
size_t _size;
};
- 插入
template<class K, class V>
bool HashTable<K, V>::Insert(const pair<K, V>& data)
{
CheckCapacity();
int id = data.first % _hs.size();//计算索引
while (_hs[id]._state == EXIST)//当前位置有元素
{
if (_hs[id]._data.first == data.first)//判读当前位置的元素的key是否和插入的数据相同
return false;
++id; //key不同,继续向后遍历,找一个空的位置
if (id == _hs.size())
id = 0;
}
_hs[id]._data = data;//元素插入
_hs[id]._state = EXIST;
++_size;
return true;
}
- 扩容
template<class K, class V>
void HashTable<K, V>::CheckCapacity()
{
if (_hs.size() == 0 || _size * 10 / _hs.size() >= 8)
{
int capacity = _hs.size() == 0 ? 10 : 2 * _hs.size();
HashTable<K, V> newhs(capacity);
for (size_t i = 0; i < _hs.size(); ++i)
{
if (_hs[i]._state == EXIST)
newhs.Insert(_hs[i]._data);
}
swap(_hs, newhs._hs);
}
}
- 搜索
template<class K, class V>
typename HashTable<K, V>:: pNode HashTable<K, V>::find(const K& key)
{
int id = key % _hs.size();
while (_hs[id]._state != EMPTY)
{
if (_hs[id]._state == EXIST)
{
if (_hs[id]._data.first == key)
return &_hs[id];
}
++id;
if (id == _hs.size())
id = 0;
}
return nullptr;
}
- 删除
template<class K, class V>
bool HashTable<K, V>::Erase(const K& key)
{
pNode pos = find(key);
if (pos)
{
pos->_state = DELETE;
--_size;
return true;
}
return false;
}