C++ 哈希表模拟实现:闭散列与冲突处理
在之前的讨论中,我们简要介绍了哈希方法及哈希表的基础概念。今天我们将深入探讨如何利用闭散列技术有效解决哈希冲突,并通过模拟实现哈希表的过程,解析这一关键技术。

一、闭散列基础
闭散列(Open Addressing):也叫开放定址法。当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把 key 存放到冲突位置中的'下一个'空位置中去。
这种方式的核心逻辑是:如果我的位置没有了,就需要去抢夺别人位置,直到找到空位为止。
1.1 线性探测
这是闭散列中最常用的一种办法。
场景: 现在需要插入元素 44,先通过哈希函数计算哈希地址,hashAddr 为 4,因此 44 理论上应该插在该位置,但是该位置已经放了值为 4 的元素,即发生哈希冲突。
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
操作方面
- 插入: 通过哈希函数获取待插入元素在哈希表中的位置。如果该位置中没有元素则直接插入新元素;如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素。
- 删除: 采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素。若直接删除元素会影响其他元素的搜索。比如删除元素 4,如果直接删除掉,44 查找起来可能会受影响(因为查找 44 时会经过 4 的位置)。因此线性探测采用标记的伪删除法来删除一个元素。
状态标记
由于采用闭散列处理哈希冲突,如果直接删除元素会影响其他元素查找,同时在插入数据中我们需要一个状态标识判断该位置是否存在数据,是否可以在该位置进行插入逻辑。同时需要注意删除元素设置状态应该为删除,而不是为空,以满足实际的状态需求。
enum State { EMPTY, EXIST, DELETE };
二、实现哈希表
2.1 哈希基本构架
哈希是通过哈希函数使得元素的存储位置与它的关键码之间能够建立一一映射的关系,需要使用 pair<K, V> 类型进行存储。采用 vector 作为底层逻辑,存储元素类型为哈希节点类型 HashData<K, V>。
这里不采用 size 作为哈希表中有效元素个数,考虑到容器中结构的差异性,是由于 _size 一般用于序列式容器中表示有效元素个数,在关联式容器中命名约定一般规定 _n 作为记录有效元素个数。
template<class K, class V>
struct HashData {
pair<K, V> _kv;
State _state = EMPTY;
};
template<class K, class >
{
:
:
vector<HashData<K, V>> _tables;
_n = ;
};



