ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

hashtable 一种根据key值直接进行访问的数据结构

2021-12-16 16:00:01  阅读:206  来源: 互联网

标签:10 include hash 拉链 int hashtable key 取余 数据结构


hash是什么

哈希用来将很大范围的数(比如[10^-9, 109]),映射到一块较小的区间内。比如对于109,我们想让它映射到[0, 105]这块区间(也可以理解为数组)内,可以直接对109进行取余(10^9 % 10^5),然后根据余数确定该数在区间内的落点。这里的取余操作就叫做hash,取余也是常见的一种hash算法。

我们使用hash就是为了节约空间,如果内存大小无限,我们直接选择开一块2 * 10^9的数据存放数据即可,但是这块内存可能有很多空闲区域。实际中内存是有限的,我们需要尽可能的将大范围的数缩小范围,聚集在一起。哈希本身是一种特殊的离散化。

对于hash表来说,查找插入和删除的效率是O(1),这是一个期望值(平均值),指的是在hash算法不会导致出现过多冲突的情况下的时间复杂度。最坏情况下是O(n)。

拉链法

既然缩小范围,那么数据量过大时,必然取余会有相同的结果,那此时再根据余数确定落点就会产生冲突。由此产生了两种解决方式:拉链法和开放寻址法。

拉链法其实就是在出现冲突的点位,延伸出一条链表,来存储相同余数的原数据。该链上所有的数hash的结果都是相同的,在查找具有重复hash值的数时,就需要遍历一下该链表,看看有无目标数据。

实际上我们会使用平衡树代替拉链,来减少冲突时的寻找时间。

开放寻址法

开放寻址法其实就是向后寻找位置,当hash结果出现冲突,如果右边的点位是空闲的,那么就继续向后寻找,直到有空闲点位。只要是hash完了发现数组的该位置已经被占,那就向后找,直到有空地。在整体上看该数组,每一小段区间内hash结果是相同的,也可能不同小区间的hash结果也相同,这取决于插入顺序。

具体实现

参考例题:活动 - AcWing

拉链法:

#include<iostream>
#include<cstring>

using namespace std;

const int N = 100003;
int h[N], e[N], ne[N], idx; 

// 求比1e5大的第一个质数,数学结果显示,对质数取余的hash冲突概率最小
// void getN() {
//     for (int i = 100000; ; i ++) {
//         bool flag = false;
//         for (int j = 2; j * j <= i; j ++ ) {
//             if (i % j != 0) {
//                 flag = true;
//             } else {
//                 flag = false;
//                 break;
//             }
//         }
//         if (flag) {
//             cout << i;
//             break;
//         }
//     }
// }

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

void find(int x) {
    int k = ((x % N) + N) % N;
    for (int i = h[k]; i != - 1; i = ne[i]) {
        if (e[i] == x) {
            cout << "Yes" << endl;
            return;
        }
    }
    cout << "No" << endl;
}

int main() {
    
    // 初始化h[N]为-1
    memset(h, -1, sizeof h);
    
    int n;
    scanf("%d", &n);
    
    while (n -- ) {
        char op[2]; int x;
        scanf("%s%d", op, &x);
        if (op[0] == 'I') {
            insert(x);
        } else {
            find(x);
        }
    }
    
    return 0;
}

开放寻址法:

#include<iostream>
#include<cstring>

using namespace std;

const int N = 100003, null = 0x3f3f3f3f;
int h[N];

void insert(int x) {
    int k = ((x % N) + N) % N;
    while(h[k] != null) {
        k ++;
        if (k == N) k = 0;
    }
    h[k] = x;
}

void find(int x) {
    int k = ((x % N) + N) % N;
    while (h[k] != null) {
        if (h[k] == x) {
            cout << "Yes" << endl;
            return;
        } else {
            k ++;
        }
        if (k == N) k = 0;
    }
    cout << "No" << endl;
}

int main() {
    
    // 初始化h[N]为比10^9大的数,表示该位置没被命中过
    // 关于0x3f的用法详见:https://www.cnblogs.com/handsomecui/p/4723949.html
    memset(h, 0x3f, sizeof h);
    
    int n;
    scanf("%d", &n);
    
    while (n -- ) {
        char op[2]; int x;
        scanf("%s%d", op, &x);
        if (op[0] == 'I') {
            insert(x);
        } else {
            find(x);
        }
    }
    
    return 0;
}

标签:10,include,hash,拉链,int,hashtable,key,取余,数据结构
来源: https://blog.csdn.net/weixin_43237362/article/details/121976521

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有