哈希散列表

哈希专题

哈希表定义

哈希表,又称为散列表,是一种根据键来直接访问内存位置的一种数据结构。它通过一个计算键值的函数(散列函数)来将所查询的数据映射到哈希表中的一个位置来查找该位置的内容,从而达到快速查找的目的。(存放记录的数组就称为哈希表)。

  1. **哈希函数:**每个元素原始值设为x,通过哈希函数h,我们可以得到一个映射值hash(x),记作 k = hash(x) 。
  2. **散列表:**以每个元素逗得哈希值的哈希值作为下标 ,对应到散列表当中去。
  3. **冲突:**若不同的元素所映射的哈希值不同,则在散列表中同一位置产生了多个元素的冲突。
  4. **冲突处理:**根据不同的哈希表设置,有不同的冲突处理方式。

整数哈希

整数哈希共有两种哈希表设置方法,一种叫拉链法,另一种叫开放寻址法
两种方法所共有的哈希函数在做算法题时都是取模。
h a s h ( x ) = ( x % N + N ) % N hash(x) = (x\%N+N)\%N hash(x)=(x%N+N)%N
一般来说,进行取模的数字N都是质数

拉链法

在这里插入图片描述

拉链法的主要逻辑是,把散列表h[]当作一个杆,赶上每个节点以下都挂着一个链表,h[i]记录的就是第i个链表的尾地址,换句话说就是h[]数组是链表尾指针。
而对于每个链表所存储的值都是映射到h[i]位置上的值。
所以显而易见,拉链法处理冲突的逻辑就是,在每一个散列表的尾部插进去这个值。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100003;
/*
!!!
注意还不会手写数组模拟链表的同学去学习这部分内容
*/
int h[N] , e[N] , ne[N];
int idx;

void insert(int x){
    int k = (x % N + N) % N ; // 哈希函数
    e[idx] = x ;                //尾插过程
    ne[idx] = h[k] ;
    h[k] = idx ++ ;
}

bool Find(int x){
    int k = (x % N + N) % N ; 
    auto it = h[k] ;        //遍历链表 找到返回true 
    while ( it != -1 ){
        if(e[it] == x){
            return true ;
        }
        it = ne[it] ;
    }
    return false ;
}

int main(){
    int n;
    scanf("%d", &n);
    memset(h, -1, sizeof h);
    while (n -- ){
        string s ;
        cin >> s;
        int x;
        scanf("%d", &x);
        if(s == "I"){
            insert(x) ;  
        }
        else {
            bool tag = Find(x);
            if(tag) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

开放寻址法

一次指针

开放寻址:一次指针

开放寻址法的h[]是用来存储每个元素原本的值,如果产生了冲突,则指针依次地向后移动,直到找到元素的位置或者没有放过元素的位置

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200003 ;
const int inf = 0x3f3f3f3f ;
int h[N] ;

int Find(int x){
    int k = (x % N + N) % N ;
    int add  = 0 ;
    while (h[k + add] != inf && h[k + add] != x){ // 找到一个位置要么没有冲突,要么是插入寻找元素本身
        add ++ ;
        if(k + add == N){
            add = -k ;
        }
    }
    int pos = add + k ;
    return pos ;
}


int main(){
    int n ;
    scanf("%d", &n);
    memset(h, 0x3f, sizeof h);
    while (n -- ){
        string s ;
        int x ;
        cin >> s >> x ;
        if(s == "I"){
            int pos = Find (x) ;
            if(h[pos] == inf ){
                h[pos] = x ;
            }
        }
        else {
            int pos = Find (x) ;
            if(h[pos] == inf ){
                puts("No");
            }
            else {
                puts("Yes");
            }
        }
    }
    return 0 ;
}

二次指针

二次指针与一次指针地区别在于:一次指针处理冲突时是向后挨个遍历,而二次指针是前后2的幂指数倍寻找
如图:
开放寻址:二次指针
红色的是寻址次数,绿色的是指针所指位置。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200003 ;
const int inf = 0x3f3f3f3f ;
int h[N] ;

int Find(int x){
    int k = (x % N + N) % N ;
    int add  = 0 ;
    while (h[k + add] != inf && h[k + add] != x){
        if(add == 0){
            add = 1 ;
            continue ;
        }
        else if(add < 0){
            add *= 2;
        }
        add = -add ;
    }
    int pos = add + k ;
    return pos ;
}


int main(){
    int n ;
    scanf("%d", &n);
    memset(h, 0x3f, sizeof h);
    while (n -- ){
        string s ;
        int x ;
        cin >> s >> x ;
        if(s == "I"){
            int pos = Find (x) ;
            if(h[pos] == inf ){
                h[pos] = x ;
            }
        }
        else {
            int pos = Find (x) ;
            if(h[pos] == inf ){
                puts("No");
            }
            else {
                puts("Yes");
            }
        }
    }
    return 0 ;
}

要注意两种开放寻址法都要把h[]数组的开辟到数据范围的两至三倍

字符串哈希

字符串哈希主要解决子串是否相等的问题。主要思路以后更新。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005 ;
const int P = 131 ;
int h[N] , p[N];
char str[N];

int get(int r , int l){
    return h[r] - h[l - 1] * p[r - l + 1] ;
}

int main(){
    int n ,m;
    cin >> n >> m ;
    scanf("%s",str + 1) ;
    p[0] = 1 ;
    for(int i = 1 ; i <= n ; i ++ ){
        p[i] = p[i-1] * P ;
        h[i] = h[i-1] * P + str[i] ;
    }
    
    while (m -- ){
        int l1 , r1 , l2 , r2 ;
        cin >> l1 >> r1 >> l2 >> r2 ;
        if(get(r1 , l1) == get(r2 , l2)){
            puts("Yes");
        }
        else {
            puts("No");
        }
    }
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值