链地址法(哈希桶)详解与C++实现
创作时间:
作者:
@小白创作中心
链地址法(哈希桶)详解与C++实现
引用
CSDN
1.
https://m.blog.csdn.net/2301_82135086/article/details/144973584
链地址法(哈希桶)
链地址法是一种解决哈希冲突的方法。与开放定址法不同,链地址法中所有的数据不再直接存储在哈希表中,而是通过指针将冲突的数据链接成一个链表,挂在哈希表的相应位置下。这种方法也被称为拉链法或哈希桶。
负载因子与扩容机制
链地址法的负载因子没有限制,可以大于1。负载因子越大,哈希冲突的概率越高,但空间利用率也越高;负载因子越小,哈希冲突的概率越低,但空间利用率也越低。在STL中,unordered_xxx容器的最大负载因子通常控制在1,当负载因子大于1时就会触发扩容操作。
示例演示
下面演示一组数值{19, 30, 5, 36, 13, 20, 21, 12, 24, 96}映射到M=11的哈希表中的过程:
- h(19) = 8
- h(30) = 8
- h(5) = 5
- h(36) = 3
- h(13) = 2
- h(20) = 9
- h(21) = 10
- h(12) = 1
- h(24) = 2
- h(96) = 8
C++代码实现
链地址法的C++代码实现如下:
namespace hash_bucket
{
// 仿函数
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
// string类型的特化版本
template<>
struct HashFunc<string>
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto ch : s)
{
hash += ch;
}
return hash;
}
};
// 哈希表节点结构
template<class K, class V>
struct HashNode
{
pair<K, V> _kv;
HashNode<K, V>* _next;
HashNode(const pair<K, V>& kv)
: _kv(kv)
, _next(nullptr)
{}
};
// 哈希表类
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
public:
HashTable()
: _tables(__stl_next_prime(0))
, _n(0)
{}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] = nullptr;
}
}
Hash hash;
Node* Find(const K& key)
{
size_t hashi = hash(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (key == cur->_kv.first)
{
return cur;
}
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
size_t hashi = hash(key) % _tables.size();
Node* cur = _tables[hashi];
Node* prev = nullptr;
while (cur)
{
if (key == cur->_kv.first)
{
if (prev == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
--_n;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
bool Insert(const pair<K, V>& kv)
{
if (_n == _tables.size())
{
vector<Node*> newtables(__stl_next_prime(_tables.size()) + 1);
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hash(cur->_kv.first) % newtables.size();
cur->_next = newtables[hashi];
newtables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newtables);
}
size_t hashi = hash(kv.first) % _tables.size();
Node* newnode = new Node(kv);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return true;
}
private:
vector<Node*> _tables;
size_t _n = 0;
};
}
实现细节
- 在扩容时,我们创建一个新的vector而不是复用Insert函数,因为这样可以避免不必要的节点创建和释放操作。
- 如果链表过长,可以考虑使用红黑树等其他数据结构来优化性能。
- 由于vector存储的是内置类型Node*,因此需要手动处理析构、拷贝构造和赋值重载等操作。
热门推荐
2024年末车企裁员潮:国际形势下的挑战与转型
车撞人保险理赔流程及赔付标准
ATM系统界面设计详解:从用户交互到技术创新
AI变革科研模式,催生跨界科学家
中华烟的前世今生,从经典到现代的演变
黄岩打造世界蜜橘文化高地
JIPB | 西南大学柑桔研究所与合作者为柑橘物种的起源和进化提供了新见解
2024年全球能源监测报告:油气开采加速向海上转移
C语言如何函数式编程
家庭影院装修与KTV包房设计流程攻略:音响效果提升秘诀
糖尿病患者注意,运动虽好,最好牢记“三不”原则,以防意外发生
强直性脊柱炎患者必看的5个生活建议,轻松缓解症状!
你有注意看孩子的角膜曲率吗?没想到它这么重要!
香港高才通计划深度解析:高学历人才赴港机遇与挑战并存
如何安全有效地邮寄相关材料?邮寄材料的过程中需要注意哪些问题?
自制家常羊肉火锅底料的完整配方与制作步骤
嘉定高中升学率排名榜,探寻区域教育新高度
银行白银投资的风险控制与投资策略?
了不起的甲骨文丨跟随甲骨文 赴一场中原博物馆之旅
探秘广元—品味广元知名小吃,解锁地方特色美食密码
康乃馨的光照需求(喜阴还是喜阳光)
如何监控员工聊天记录:平衡企业安全与员工隐私
研究证实:铅暴露与男性冠状动脉钙化密切相关
铅含量测试限值一般是多少?全面解读铅含量标准与检测方法
镜里千秋——中国古代铜镜文化
新手学吉他基础入门教程
紫外线窗户贴膜:保护皮肤免受阳光伤害的重要防线
一针70万,医保谈判到3万!澳洲仅184块,为何还说中国价最低?
未婚未育的90后,开始抢着当月嫂?
马来西亚第二家园的中国移民,都在经营哪些生意?