C++哈希表实现:开散列与闭散列详解

好的,我们来详细讲解C++中哈希表的两种常见实现方式:开散列 (Open Hashing / Separate Chaining)和闭散列(Closed Hashing / Open Addressing)。


哈希表基础

哈希表通过哈希函数将键映射到数组中的索引位置。理想情况下,每个键映射到唯一索引,但不同键可能映射到相同位置,称为哈希冲突。解决冲突是哈希表实现的关键。


1. 闭散列(Open Addressing)

闭散列中,所有元素都存储在数组本身中。发生冲突时,按照特定规则(探测序列)寻找下一个空槽位。

常见探测方法
  • 线性探测 :冲突后,顺序检查下一个槽位(索引+1)。
    • 公式:h_i(k) = (h(k) + i) \\mod N,其中 N 为表大小。
  • 二次探测 :避免线性探测的聚集问题,探测步长为平方。
    • 公式:h_i(k) = (h(k) + c_1 i + c_2 i\^2) \\mod N
  • 双重哈希 :使用第二个哈希函数计算步长。
    • 公式:h_i(k) = (h_1(k) + i \\cdot h_2(k)) \\mod N
特点
  • 优点
    • 内存连续,缓存友好。
    • 无需额外链表结构。
  • 缺点
    • 装载因子(\\alpha = \\frac{\\text{元素数}}{\\text{表大小}})需严格控制(通常 \\alpha \< 0.7),否则性能下降。
    • 删除操作复杂(需标记为"已删除"而非直接清空)。
示例代码(线性探测)
cpp 复制代码
class HashTable {
private:
    vector<int> table; // 存储数据
    vector<bool> deleted; // 标记删除状态
    size_t capacity;
    size_t size = 0;

    size_t hash(int key) {
        return key % capacity;
    }

public:
    HashTable(size_t cap) : capacity(cap) {
        table.resize(cap, -1); // -1表示空槽
        deleted.resize(cap, false);
    }

    bool insert(int key) {
        if (size >= capacity) return false;
        size_t idx = hash(key);
        while (table[idx] != -1 && !deleted[idx]) { // 寻找空槽或已删除槽
            if (table[idx] == key) return false; // 键已存在
            idx = (idx + 1) % capacity;
        }
        table[idx] = key;
        deleted[idx] = false;
        size++;
        return true;
    }

    bool find(int key) {
        size_t idx = hash(key);
        size_t start = idx;
        do {
            if (table[idx] == key && !deleted[idx]) return true;
            if (table[idx] == -1 && !deleted[idx]) break; // 遇到未删除的空槽
            idx = (idx + 1) % capacity;
        } while (idx != start);
        return false;
    }

    bool erase(int key) {
        size_t idx = hash(key);
        size_t start = idx;
        do {
            if (table[idx] == key && !deleted[idx]) {
                deleted[idx] = true; // 标记删除
                size--;
                return true;
            }
            if (table[idx] == -1 && !deleted[idx]) break;
            idx = (idx + 1) % capacity;
        } while (idx != start);
        return false;
    }
};

2. 开散列(Separate Chaining)

开散列中,每个数组槽位是一个链表(或其它容器)。冲突时,元素直接添加到对应链表中。

特点
  • 优点
    • 装载因子可更高(\\alpha \> 1 仍有效)。
    • 删除操作简单(直接移除链表节点)。
  • 缺点
    • 指针开销大,内存碎片化。
    • 缓存不友好(链表节点可能分散)。
示例代码
cpp 复制代码
class HashTable {
private:
    vector<list<int>> buckets; // 每个桶是一个链表
    size_t capacity;

    size_t hash(int key) {
        return key % capacity;
    }

public:
    HashTable(size_t cap) : capacity(cap) {
        buckets.resize(cap);
    }

    bool insert(int key) {
        size_t idx = hash(key);
        for (auto it = buckets[idx].begin(); it != buckets[idx].end(); ++it) {
            if (*it == key) return false; // 键已存在
        }
        buckets[idx].push_back(key);
        return true;
    }

    bool find(int key) {
        size_t idx = hash(key);
        for (int val : buckets[idx]) {
            if (val == key) return true;
        }
        return false;
    }

    bool erase(int key) {
        size_t idx = hash(key);
        for (auto it = buckets[idx].begin(); it != buckets[idx].end(); ++it) {
            if (*it == key) {
                buckets[idx].erase(it);
                return true;
            }
        }
        return false;
    }
};

对比与选择

特性 闭散列 开散列
内存布局 连续数组 数组+链表/树
装载因子限制 较低(通常 \\alpha \< 0.7 较高(可 \\alpha \> 1
删除复杂度 需特殊标记 直接删除节点
缓存友好度
实现难度 中等(需处理探测序列) 简单
适用场景
  • 闭散列:对缓存性能要求高、内存受限的场景(如嵌入式系统)。
  • 开散列 :通用场景,标准库(如 std::unordered_map)常用此实现。

总结

  • 闭散列通过探测序列解决冲突,适合内存紧凑的场景。
  • 开散列通过链表解决冲突,扩展性强,易于实现。
  • 实际选择需权衡内存效率性能需求实现复杂度
相关推荐
weixin_452159552 小时前
C++与Java性能对比
开发语言·c++·算法
Timmylyx05182 小时前
类欧几里得学习笔记
笔记·学习·算法
wangluoqi2 小时前
26.2.2练习总结
算法
2301_765703142 小时前
C++中的工厂模式实战
开发语言·c++·算法
星火开发设计3 小时前
C++ 输入输出流:cin 与 cout 的基础用法
java·开发语言·c++·学习·算法·编程·知识
We་ct3 小时前
LeetCode 289. 生命游戏:题解+优化,从基础到原地最优
前端·算法·leetcode·矩阵·typescript
自己的九又四分之三站台3 小时前
9:MemNet记忆层使用,实现大模型对话上下文记忆
人工智能·算法·机器学习
LXS_3573 小时前
STL - 函数对象
开发语言·c++·算法
aini_lovee3 小时前
基于粒子群算法(PSO)优化BP神经网络权值与阈值的实现
神经网络·算法