返回首页 C++ 模板

哈希表

键值映射、O(1)查找、冲突处理——从原理到实现

哈希表 哈希函数 冲突处理 GESP7
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;

// ========== 1. 开放寻址法 ==========
const int null_val = 0x3f3f3f3f;
int h[N];  // 哈希表

void hash_init() {
    memset(h, 0x3f, sizeof(h));  // 初始化为"空"
}

int hash_find(int x) {
    int k = (x % N + N) % N;  // 哈希函数,处理负数
    while (h[k] != null_val && h[k] != x) {
        k++;
        if (k == N) k = 0;  // 循环
    }
    return k;  // 如果 h[k]==null_val 说明不存在,否则是 x 的位置
}

void hash_insert(int x) {
    int k = hash_find(x);
    if (h[k] == null_val) h[k] = x;
}

bool hash_contains(int x) {
    int k = hash_find(x);
    return h[k] != null_val;
}

// ========== 2. 拉链法(链地址法)==========
int h2[N], e[N], ne[N], idx = 0;

void chain_init() {
    memset(h2, -1, sizeof(h2));
}

void chain_insert(int x) {
    int k = (x % N + N) % N;
    e[idx] = x;
    ne[idx] = h2[k];
    h2[k] = idx++;
}

bool chain_find(int x) {
    int k = (x % N + N) % N;
    for (int i = h2[k]; i != -1; i = ne[i]) {
        if (e[i] == x) return true;
    }
    return false;
}

// ========== 3. STL unordered_map / unordered_set ==========
void stl_hash_demo() {
    // 哈希映射(键值对)
    unordered_map<string, int> score;
    score["Alice"] = 95;
    score["Bob"] = 87;

    cout << score["Alice"] << endl;  // 95

    // 判断键是否存在
    if (score.count("Charlie")) {
        cout << score["Charlie"] << endl;
    }

    // 遍历
    for (auto &[name, s] : score) {
        cout << name << ": " << s << endl;
    }

    // 哈希集合(去重)
    unordered_set<int> seen;
    seen.insert(42);
    seen.insert(42);  // 重复插入无效
    cout << seen.count(42) << endl;  // 1
    cout << seen.size() << endl;     // 1

    // 统计每个数出现次数
    unordered_map<int, int> cnt;
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
    for (int x : arr) cnt[x]++;
    for (auto &[val, c] : cnt) {
        cout << val << " 出现 " << c << " 次" << endl;
    }
}

// ========== 4. 字符串哈希 ==========
// 用于快速比较两个子串是否相同
typedef unsigned long long ULL;
const int BASE = 131;  // 哈希基数
ULL p[N], h_str[N];    // p[i] = BASE^i, h_str[i] = 前缀哈希

void str_hash_init(string &s) {
    int n = s.size();
    p[0] = 1;
    h_str[0] = 0;
    for (int i = 1; i <= n; i++) {
        h_str[i] = h_str[i-1] * BASE + s[i-1];
        p[i] = p[i-1] * BASE;
    }
}

// 获取子串 s[l..r] 的哈希值(1-indexed)
ULL get_hash(int l, int r) {
    return h_str[r] - h_str[l-1] * p[r-l+1];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // 数组哈希:判断元素是否存在
    hash_init();
    hash_insert(42);
    hash_insert(17);
    cout << hash_contains(42) << endl;  // 1
    cout << hash_contains(99) << endl;  // 0

    // 字符串哈希
    string s = "hello world";
    str_hash_init(s);
    // 比较子串是否相同
    cout << (get_hash(1, 3) == get_hash(7, 9) ? "相同" : "不同") << endl;

    return 0;
}

📖 要点说明

哈希表核心

两种冲突处理

方式 思路 优点 缺点
开放寻址 冲突就往后找空位 缓存友好 聚集效应
拉链法 同一位置挂链表 空间灵活 指针开销

字符串哈希

STL 选择

⚠️ 常见错误