代码模板6---数据结构(哈希表/C++STL)

本文介绍了哈希的常见方法,包括取模、拉链法和开放寻址法,并提供了相关代码示例。同时,文章探讨了在算法竞赛中设置无穷大的方法。此外,还详细阐述了C++STL中的几种容器,如vector、pair、string、queue、priority_queue、stack、deque、set和map,以及它们的基本操作和特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、哈希

①常见哈希方式

        注意点:

①取模的模数,一般是质数(不容易冲突)–>搜一下,且一般先模再加再模(处理负数)(讲究顺序,例:-10的9次方先加N即10的5次方的话再模,还是负数)
②拉链法:头插法(数组模拟)
③开放寻址法(蹲坑法): 通常开数组为目标数组大小的2-3倍,
find函数的功能(寻找坑位),
其返回值: 若原本存在,返回存在位置的下标,若原本不存在,返回可以插入的位置下标。
④有关无穷大设置:
在算法竞赛中,我们常采用0x3f3f3f3f来作为无穷大。0x3f3f3f3f主要有如下好处:
0x3f3f3f3f的十进制为1061109567,和INT_MAX一个数量级,即10^9数量级,而一般场合下的数据都是小于10^9的。0x3f3f3f3f * 2 = 2122219134,无穷大相加依然不会溢出。可以使用
memset(array, 0x3f,sizeof(array))来为数组设初值为0x3f3f3f3f,因为这个数的每个字节都是0x3f
写代码时的注意事项:
①拉链法:比较值的大小的时候,比较的是e数组的里面的值,而非h数组里面的值(存放的是首地址)
②开放寻址法:注意find寻找的终止条件是&&
(1) 拉链法
    int h[N], e[N], ne[N], 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;
        for (int i = h[k]; i != -1; i = ne[i])
            if (e[i] == x)
                return true;

        return false;
    }

(2) 开放寻址法
    int h[N];

    // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
    int find(int x)
    {
        int t = (x % N + N) % N;
        while (h[t] != null && h[t] != x)
        {
            t ++ ;
            if (t == N) t = 0;
        }
        return t;
    }

        1.1 开放寻址法

//法一:拉链法
#include<iostream>
#include<cstring>
using namespace std;
const int N=100003;
int h[N],idx,e[N],ne[N];
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;
    for(int i=h[k];i!=-1;i=ne[i])
        if(e[i]==x)  //注意,hash表中存储的头结点的地址,比较值的大小需要用的是e数组
            return true;
    return false;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    int n;cin>>n;
    memset(h,-1,sizeof(h));//只需要给h初始化就好了,头文件cstring
    while(n--)
    {
        int x;string op;cin>>op>>x;
        if(op=="I")insert(x);
        else
        {
            if(find(x))cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}

       1.2 拉链法

//法二:开放寻址法(不考虑删除)
#include<iostream>
#include<cstring>
using namespace std;
const int N=300007,null=0x3f3f3f3f; //定义一个无穷大量
int h[N];
//函数功能:如果有x,返回x的下标;如果没有,返回第一个可以插入的位置
int find(int x)
{
    int k=(x%N+N)%N;
    while(h[k]!=null&&h[k]!=x)//终止条件,找到x或者找到了空位
    {
        k++;
        if(k==N)k=0;//k为0之后就重头开始
    }
    return k;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    int n;cin>>n;
    memset(h,0x3f,sizeof(h));//按字节进行初始化
    while(n--)
    {
        string op;int x;cin>>op>>x;
        if(op=="I")h[find(x)]=x;
        else 
            if(h[find(x)]==x)cout<<"Yes"<<endl;
            else    cout<<"No"<<endl;
    }
    return 0;
}

②字符串哈希841. 字符串哈希 

        模板

核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果

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

// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];
    p[i] = p[i - 1] * P;
}

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

        题解:

#include<iostream>
using namespace std;

typedef unsigned long long ULL;
const int N=100010,P=131;
char str[N];
int p[N],h[N];  //p[2]表示P的2次方

//函数功能:获得字符串子串的哈希值
ULL get(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}

int main()
{
    //ios::sync_with_stdio(false);cin.tie(0);
    int n,m;cin>>n>>m;
    scanf("%s",str+1);//细节:为了表示的统一,从下标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,l2,r1,r2;cin>>l1>>r1>>l2>>r2;
        if(get(l1,r1)==get(l2,r2)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

二、STL

vector, 变长数组,倍增的思想
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    支持比较运算,按字典序

pair<int, int>
    first, 第一个元素
    second, 第二个元素
    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)

string,字符串
    size()/length()  返回字符串长度
    empty()
    clear()
    substr(起始下标,(子串长度))  返回子串
    c_str()  返回字符串所在字符数组的起始地址

queue, 队列
    size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素

priority_queue, 优先队列,默认是大根堆
    size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

stack, 栈
    size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素

deque, 双端队列
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []

set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)

    set/multiset
        insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器
    map/multimap
        insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()

unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)
    不支持 lower_bound()/upper_bound(), 迭代器的++,--

bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []

    count()  返回有多少个1

    any()  判断是否至少有一个1
    none()  判断是否全为0

    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~
    flip(k) 把第k位取反
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Ocean__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值