题目
编写程序,探查 std::unordered_map 的底层哈希表(开链哈希桶 Hash Bucket)结构。通过调用 bucket_count()、bucket_size() 与 load_factor(),实时观测元素插入过程中当装载因子突破最大阈值(默认 max_load_factor() == 1.0)时所引发的哈希桶数激增(Rehash)现象,并打印每个桶中由于哈希冲突挂载的链表节点。
输入格式:无需外部输入。
输出格式:输出装载因子动态变化、哈希桶再分配与桶内链表冲突分布详情。
数据范围与约定:闭散列开链哈希表、平均常数时间复杂度 O(1)。
思路与算法
1. 哈希桶开链模型std::unordered_map 采用拉链法解决哈希冲突。每个桶(Bucket)是一个单向链表头指针。计算 hash(key) % bucket_count 决定存入哪一个桶。
2. 负载因子与 Rehashload_factor = size() / bucket_count()。当负载因子超过 max_load_factor 时,哈希表会自动申请约 2 倍数量的新桶,并将所有历史节点重新散列(Rehash),开销为 O(N)。
3. 预先 reserve 防抖动
对于海量已知规模的数据,预先调用 umap.reserve(expected_size),能将桶数量一次性初始化到位,避免多次 Rehash 带来的高额分配损耗。
完整程序
#include <iostream>
#include <unordered_map>
#include <string>
void inspectBuckets(const std::unordered_map<int, std::string>& map, const std::string& tag) {
std::cout << "=== [" << tag << "] ===\n";
std::cout << "元素总数 (size): " << map.size()
<< ", 哈希桶总数 (bucket_count): " << map.bucket_count() << "\n";
std::cout << "当前负载因子是否超过阈值: " << std::boolalpha
<< (map.load_factor() > map.max_load_factor()) << "\n";
size_t nonempty = 0;
size_t maxChain = 0;
for (size_t b = 0; b < map.bucket_count(); ++b) {
size_t n = map.bucket_size(b);
if (n > 0) ++nonempty;
if (n > maxChain) maxChain = n;
}
std::cout << "非空桶个数: " << nonempty << ", 最长链: " << maxChain << "\n\n";
}
int main() {
std::unordered_map<int, std::string> studentMap;
// 初始状态
inspectBuckets(studentMap, "初始空表");
// 连续插入促使负载因子升高并触发 Rehash
for (int i = 1; i <= 15; ++i) {
studentMap[i] = "Student_" + std::to_string(i);
if (i == 4 || i == 10 || i == 15) {
inspectBuckets(studentMap, "插入 " + std::to_string(i) + " 个元素后");
}
}
return 0;
}运行示例
输出:
=== [初始空表] ===
元素总数 (size): 0, 哈希桶总数 (bucket_count): 1
当前负载因子是否超过阈值: false
非空桶个数: 0, 最长链: 0
=== [插入 4 个元素后] ===
元素总数 (size): 4, 哈希桶总数 (bucket_count): 13
当前负载因子是否超过阈值: false
非空桶个数: 4, 最长链: 1
=== [插入 10 个元素后] ===
元素总数 (size): 10, 哈希桶总数 (bucket_count): 13
当前负载因子是否超过阈值: false
非空桶个数: 10, 最长链: 1
=== [插入 15 个元素后] ===
元素总数 (size): 15, 哈希桶总数 (bucket_count): 29
当前负载因子是否超过阈值: false
非空桶个数: 15, 最长链: 1