Skip to content

哈希

一般哈希

模板题:【哈希表】模拟散列表「哈希表基础」

素数选取表:

下界上界素数
2^52^653
2^62^7101
2^72^8193
2^82^9389
2^92^10769
2^102^111531
2^112^123061
2^122^136113
2^132^1412253
2^142^1524379
2^152^1648883
2^162^1797787
2^172^18195677
2^182^19391627
2^192^20783259
2^202^211566401
2^212^223133987
2^222^236269119
2^232^2412538073
2^242^2525082363
2^252^2650170979
2^262^27100353503
2^272^28200730139
2^282^29401498927
2^292^30803081491

开放寻址法

C++
// 一般N取比数据范围大两倍的素数 INF取0x3f3f3f3f
int ht[N];

// 把ht中的值全部初始化为INF
memset(ht, 0x3f, sizeof(ht));

// 如果x在哈希表中就返回x的下标 否则返回x应该插入的位置
int find(int x) {
    int y = (x % N + N) % N;
    while (ht[y] != INF && ht[y] != x) {
        if (++y == N) y = 0;
    }
    return y;
}
// 一般N取比数据范围大两倍的素数 INF取0x3f3f3f3f
int ht[N];

// 把ht中的值全部初始化为INF
memset(ht, 0x3f, sizeof(ht));

// 如果x在哈希表中就返回x的下标 否则返回x应该插入的位置
int find(int x) {
    int y = (x % N + N) % N;
    while (ht[y] != INF && ht[y] != x) {
        if (++y == N) y = 0;
    }
    return y;
}

拉链法

拉链的方法与单链表向头节点前插入相同。

C++
// 一般N取两到三倍数据范围的素数
int ht[N], vals[N], nexts[N], idx;

// 初始化
void init() {
    idx = 0;
    memset(ht, -1, sizeof(ht));
}

// 向哈希表中插入一个数
void insert(int x) {
    int y = (x % N + N) % N;
    // 从单链表头插入
    vals[idx] = x;
    nexts[idx] = ht[y];
    ht[y] = idx++;
}

// 在哈希表中查询某个数是否存在
bool find(int x) {
    int y = (x % N + N) % N;
    for (int i = ht[y]; i != -1; i = nexts[i]) {
        if (vals[i] == x) return true;
    }
    return false;
}
// 一般N取两到三倍数据范围的素数
int ht[N], vals[N], nexts[N], idx;

// 初始化
void init() {
    idx = 0;
    memset(ht, -1, sizeof(ht));
}

// 向哈希表中插入一个数
void insert(int x) {
    int y = (x % N + N) % N;
    // 从单链表头插入
    vals[idx] = x;
    nexts[idx] = ht[y];
    ht[y] = idx++;
}

// 在哈希表中查询某个数是否存在
bool find(int x) {
    int y = (x % N + N) % N;
    for (int i = ht[y]; i != -1; i = nexts[i]) {
        if (vals[i] == x) return true;
    }
    return false;
}

字符串哈希

核心思想:将字符串看成P进制数,P的经验值是 13113331,取这两个值的冲突概率低。 小技巧 - 自然溢出:取模的数用 264,这样直接用 unsigned long long (Java 用 long)存储,溢出的结果就是取模的结果,这样可以省去取模的代码和时间。

模板题:字符串哈希

C++
typedef unsigned long long ULL;

// h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
ULL h[N], p[N]; 

// 初始化
void init() {
    // s是下标从0开始的字符串 n是其长度
    p[0] = 1;
    for (int i = 1; i <= n; ++i) {
        h[i] = h[i - 1] * P + str[i - 1];
        p[i] = p[i - 1] * P;
    }
}

// 计算子串 str[l...r] 的哈希值
ULL sub_hash(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
typedef unsigned long long ULL;

// h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
ULL h[N], p[N]; 

// 初始化
void init() {
    // s是下标从0开始的字符串 n是其长度
    p[0] = 1;
    for (int i = 1; i <= n; ++i) {
        h[i] = h[i - 1] * P + str[i - 1];
        p[i] = p[i - 1] * P;
    }
}

// 计算子串 str[l...r] 的哈希值
ULL sub_hash(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}