数据结构(三)哈希表 、STL使用技巧

本文详细讲解了哈希表的数据结构及其拉链法、开放寻址法实现,以及字符串哈希的应用,包括如何通过哈希函数解决集合查询问题和字符串子串比较。通过实例演示了C++代码实现,适合理解哈希表原理和实际编程应用。

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

哈希表

例题:

拉链法 

模拟散列表

维护一个集合,支持如下几种操作:

  1. I x,插入一个数 xx;
  2. Q x,询问数 xx 是否在集合中出现过;

现在要进行 N 次操作,对于每个询问操作输出对应的结果。

输入格式

第一行包含整数 N,表示操作数量。

接下来 N 行,每行包含一个操作指令,操作指令为 I xQ x 中的一种。

输出格式

对于每个询问指令 Q x,输出一个询问结果,如果 xx 在集合中出现过,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤N≤105
−109≤x≤109

输入样例:

5
I 1
I 2
I 3
Q 2
Q 5

输出样例:

Yes
No

 解题代码:

#include <iostream>
#include <cstring>

using namespace std;

const int N=100010;

int h[N],e[N],ne[N],idx;//e存的链表的值,ne存的下一个节点的下标(next)

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])//h[k]存的是头节点指向的下一节点的下标
        if(e[i] == x)
            return true;
            
    return false;
}

int main()
{
   /* for(int i=100000;;i ++) //找到大于十万的第一个质数
    {
        bool flag = true;
        for(int j=2;j*j<=i;j++)
            if(i%j == 0)
            {
                flag = false;
                break;
            }
        if(flag)
        {
            cout << i<<endl;
            break;
        }
    }*/
    
    int n;
    scanf("%d",&n);
    
    memset(h,-1,sizeof h);//清空所有的槽
    
    while(n --)
    {
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        
        if(*op == 'I') insert(x);
        else
        {
            if(find(x)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

开放寻址法

 

 解题代码:

#include <cstring>
#include <iostream>

using namespace std;

const int N = 200003, null = 0x3f3f3f3f;

int h[N];

int find(int x)
{
    int t = (x % N + N) % N;
    while (h[t] != null && h[t] != x)//坑有人,并且不是x
    {
        t ++ ;
        if (t == N) t = 0;//坑位看完了,看第一个
    }
    return t;
}

int main()
{
    memset(h, 0x3f, sizeof h);//清空所有的槽

    int n;
    scanf("%d", &n);

    while (n -- )
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);
        if (*op == 'I') h[find(x)] = x;
        else
        {
            if (h[find(x)] == null) puts("No");
            else puts("Yes");
        }
    }

    return 0;
}

字符串哈希方式

 

 

例题:

 字符串哈希

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式

第一行包含整数 n 和 m,表示字符串长度和询问次数。

第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。

接下来 m 行,每行包含四个整数 l1,r1,l2,r2表示一次询问所涉及的两个区间。

注意,字符串的位置从 1 开始编号。

输出格式

对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤n,m≤105

输入样例:

8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2

输出样例:

Yes
No
Yes

 解题:

#include <iostream>

using namespace std;

typedef unsigned long long ULL;

const int N =100010,P=131;

int n,m;
char str[N];
ULL h[N],p[N];//p放的是多少次方

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

int main()
{
    scanf("%d%d%s",&n,&m,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;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        
        if(get(l1,r1) == get(l2,r2)) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

STL 

 

#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>

vector,变长数组,倍增的思想
        size() 返回元素个数
        empty() 返回是否为空
        clear() 清空
        front()/back() 返回vecto第一个数/返回vector最后一个数
        push_back()/pop_back() 向vector最后插入一个数/删除最后一个数
        begin()/end() begin是第0个数/end是最后一个数的后面一个
        支持随机选举
    for(int i =0;i<10;i++) a.push_back(i);
    for(int i=0;i<a.size();i++) cout << a[i]<<' ';

    for(vector<int>::iterator i=a.begin();i!=a.end();i++) cout <<*i <<' ';
    //a.begin =a[0] a.end=a[size]
    cout << endl;

    for(auto x:a) cout << x <<' ';
    cout <<endl;

        支持比较运算(按字典比较)
    vector<int> a(4,3),b(3,4);
    
    if(a<b) puts("a<b");

pair<int ,int>    pair<int ,string>p;
        first,第一个元素    p.first;
        second,第二个元素    p.second;
        支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
    
    pair<int,pair<int,int>>p;
    p=make_pair(10,"acj");
    p={20,"abc"};


string,字符串,substr(),c_str()
        size()/length() 返回元素个数/返回字符串长度
        empty() 返回是否为空
        clear() 清空
    可以直接添加字符串
    string a ="adwd";
    a+=“ddef”;
    a+='c'; cout <<a <<endl;
        substr(1,2):从下标1开始,长度是2,超出长度,输出最后一个为止
        c_str():存储字符串的起始位置的地址


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

priority_queue,优先队列,默认大根堆
        push():插入一个元素
        top():返回堆顶元素
        pop():弹出堆顶元素
    //小根堆
    priority_queue<int,vector<int>,greater<int>> heap;
    or
    priority_queue<int> heap;
    heap.push(-x);


stack,栈
        size() 返回元素个数
        empty() 返回是否为空
        push():向栈顶插入一个元素
        top():返回栈顶元素
        pop():弹出栈顶元素


deque,双端队列
        size() 返回元素个数
        empty() 返回是否为空
        clear() 清空
        front():返回第一个元素
        back():返回最后一个元素
        push_back()/pop_back():向最后插入一个元素/弹出最后一个元素
        push_front()/pop_front():向队首插入一个元素/弹出队首元素
        begin()/end() begin是第0个数/end是最后一个数的后面一个


set,map,multiset,multimap,基于平衡二叉树(红黑树),动态维护有序序列
        size() 返回元素个数
        empty() 返回是否为空
        clear() 清空
        begin()/end() ++,-- 返回前驱和后继
    set/multiset
        insert() 插入一个数
        find()    查找一个数
        count()    返回某一个数的个数
        erase()
            (1)输入是一个数x,删除所有x
            (2)输入一个迭代器,删除这个迭代器
        lower_bound()/uper_bound()
            lower_bound():返回大于等于x的最小的数的迭代器
            uper_bound():返回大于x的最小的数的迭代器
      set<int> s;//不能有重复元素
      multiset<int> ms;//可有重复的

     map/multimap   
        insert() 插入的数是一个pair
        erase()   输入的参数是pair或者迭代器
        find()    查找一个数
        能像数组一样来用,时间复杂度是o(logn)        

    map<string,int> a;
    a["asac"]=1;
    cout<<a["szc"]<<endl;


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



bitset,压位(省八位空间)
        1024个bool(一字节)
        1024 B =1KB
        如果压到每一位去,每个字节存8位,只需要开128个字节
    bitset<10000> s;
    ~,&,|,^
    >>,<<
    ==,!=
    [] 取出某一位是0,是1

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

    set() 把所有位置b变成1
    set(k,v) 把第k位变成v
    reset() 把所有位变成0
    flip() 等价于~
    flip(k) 把第k位取反

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值