哈希表和STL基础

哈希表

哈希表作用是什么?是将一个数映射为另一个数,如果说离散化是将“有序”的数进行映射,那么哈希表就是更加广义上的映射。

比如,现在有一个区间-1e9~1e9。但是真正用到的数只有1e5个。所以我们要进行映射。

其中一个方法就是取模。比如x mod 1e5。但是这样会有一个问题,就是可能会有重复的映射值。比如,1~10 mod 5。那么5 mod 5 =0。10 mod 5 = 0。这就有两个重复的数字了。那么如何解决?

方法一:“拉链法”。

例题一:
 

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

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

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

输入格式

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

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

输出格式

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

每个结果占一行。

数据范围

1≤N≤1e5
−1e9≤x≤1e9

输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No

如果有负数的话,哈希函数应该这样取值:

int map(int x){

        return (x%N +N) %N;

}

#include<iostream>
#include<cstring>
using namespace std;
const int N = 100003;
int myhash[N],e[N],ne[N],idx;

void insert(int x){
    int k = (x % N + N) % N;
    e[idx] = x;//原始值
    ne[idx] = myhash[k];
    myhash[k] = idx;
    idx++;
}

int find(int x){
    int k = (x % N + N )% N;
    for(int i = myhash[k];i != -1;i = ne[i]){
        if(e[i] == x) 
            return 1;
    }
    return -1;
}

int main(){
    int n;
    cin>>n;
    memset(myhash,-1,sizeof(myhash));
    while(n--){
        char op[2];
        int v;
        scanf("%s%d",op,&v);
        if(op[0]=='I'){
            insert(v);
        }else{
            int tag = find(v);
            if(tag != -1) puts("Yes");
            else puts("No");
        }
    }
    
}

具体讲解一下这个过程。(这里假设没有负数,N=10。且哈希函数是x%N)

初始化myhash数组:

抽象来说是这样的:

即myhash的值其实是“节点的身份证”,是“指针”。

插入20:

   20 % 10 = 0;

    下面就是插入的逻辑
    int k = (x % N + N) % N;
    e[idx] = x;//原始值
    ne[idx] = myhash[k];
    myhash[k] = idx;
    idx++;

可以看出:进行映射后,将映射后的值作为下标(或者说是指针,其真实值存储在e[idx]中)。

查找的逻辑:
int find(int x){
    int k = (x % N + N )% N;
    for(int i = myhash[k];i != -1;i = ne[i]){
        if(e[i] == x) 
            return 1;
    }
    return -1;
}

首先要找到要查找的数,经过映射后是多少。然后根据这个映射后的数(是数组的索引),查找这个映射后的数都对应着哪些数。

遍历逻辑:找到要查找的数的哈希值k,然后从hash[k]开始找,注意hash[k]存的是映射为这个数的指针(一个连着一个),因为初始时是空的(hash[k]==-1)。所以只要不等于-1就可以继续遍历。

删除逻辑:

其实只要查找x的下标i,然后将e[i] = -2;做一个标记就可以删除了。

总体逻辑:

首先将原数值ori映射为一个较小的数x(采用取模的方式),然后将这个x作为数组myhash的下标,将myhash[x]中指向值为ori的节点。说白了就是myhash中存的是指针。

接下来载看一种算法来模拟哈希表。

插入20:

插入30:

因为20和30都映射到0号位置,但是先插入的20,所以30要从20所在位置依次向后面找一个非空的位置。

#include<iostream>
#include<cstring>
using namespace std;
const int N = 100003;
int myhash[2*N],inf = 0x3f3f3f3f;


int find(int x){//如果存在,则返回所在的位置;如果不存在则返回应在存在的位置。
    int k = (x % N + N )% N;
    while(myhash[k] != x && myhash[k] != inf){//当前位置不等于x,并且当前位置不等于空
        k++;
        if(k==N) k = 0;
    }
    return k;
}

int main(){
    int n;
    cin>>n;
    memset(myhash,inf,sizeof(myhash));
    while(n--){
        char op[2];
        int v;
        scanf("%s%d",op,&v);
        if(op[0]=='I'){
            int k = find(v);
            myhash[k] = v;
        }else{
            int k = find(v);
            if(myhash[k] != inf) puts("Yes");
            else puts("No");
        }
    }
    
}

注意find函数返回一个位置,如何存在这个数就返回其所在位置,否则返回其应该在的位置。

int find(int x){//如果存在,则返回所在的位置;如果不存在则返回应在存在的位置。
    int k = (x % N + N )% N;
    while(myhash[k] != x && myhash[k] != inf){//当前位置不等于x,并且当前位置不等于空
        k++;
        if(k==N) k = 0;
    }
    return k;
}

STL基础

1. vector

用法小概括:
  • 创建vector<type> vec; 或 vector<type> vec(size);
  • 添加元素vec.push_back(value);
  • 访问元素vec[index] 或 vec.at(index)
  • 删除元素vec.pop_back(); 或 vec.erase(iterator);
  • 插入元素vec.insert(position, value);
  • 大小vec.size() 返回 size_t
  • 清空vec.clear()
代码实战:
#include <iostream>
#include <vector>

using namespace std;

int main() {
    // 创建一个 vector
    vector<int> vec;

    // 添加元素
    vec.push_back(1);  // vec = [1]
    vec.push_back(2);  // vec = [1, 2]

    // 访问元素
    int first = vec[0];  // first = 1
    int second = vec.at(1);  // second = 2

    // 插入元素
    vec.insert(vec.begin() + 1, 3);  // vec = [1, 3, 2]

    // 删除元素
    vec.pop_back();  // vec = [1, 3]

    // 清空
    vec.clear();  // vec = []

    // 输出
    cout << "Size: " << vec.size() << endl;  // Size: 0
    return 0;
}

运行结果

Size: 0


2. stack

用法小概括:
  • 创建stack<type> stk;
  • 添加元素stk.push(value);
  • 访问元素stk.top() 返回 type&
  • 删除元素stk.pop();
  • 大小stk.size() 返回 size_t
  • 是否为空stk.empty() 返回 bool
代码实战:
#include <iostream>
#include <stack>

using namespace std;

int main() {
    // 创建一个 stack
    stack<int> stk;

    // 添加元素
    stk.push(1);  // stk = [1]
    stk.push(2);  // stk = [1, 2]

    // 访问元素
    int top = stk.top();  // top = 2

    // 删除元素
    stk.pop();  // stk = [1]

    // 输出
    cout << "Top: " << top << endl;  // Top: 2
    cout << "Size: " << stk.size() << endl;  // Size: 1
    cout << "Empty: " << stk.empty() << endl;  // Empty: 0 (false)

    return 0;
}

运行结果

Top: 2
Size: 1
Empty: 0


3. queue

用法小概括:
  • 创建queue<type> q;
  • 添加元素q.push(value);
  • 访问元素q.front() 返回 type&
  • 删除元素q.pop();
  • 大小q.size() 返回 size_t
  • 是否为空q.empty() 返回 bool
代码实战:
#include <iostream>
#include <queue>

using namespace std;

int main() {
    // 创建一个 queue
    queue<int> q;

    // 添加元素
    q.push(1);  // q = [1]
    q.push(2);  // q = [1, 2]

    // 访问元素
    int front = q.front();  // front = 1

    // 删除元素
    q.pop();  // q = [2]

    // 输出
    cout << "Front: " << front << endl;  // Front: 1
    cout << "Size: " << q.size() << endl;  // Size: 1
    cout << "Empty: " << q.empty() << endl;  // Empty: 0 (false)

    return 0;
}

运行结果

Front: 1
Size: 1
Empty: 0


4. priority_queue

用法小概括:
  • 创建priority_queue<type> pq; 或 priority_queue<type, vector<type>, greater<type>> pq; (小根堆)
  • 添加元素pq.push(value);
  • 访问元素pq.top() 返回 type&
  • 删除元素pq.pop();
  • 大小pq.size() 返回 size_t
  • 是否为空pq.empty() 返回 bool
代码实战(大根堆和小根堆):
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main() {
    // 大根堆
    priority_queue<int> maxHeap;
    maxHeap.push(1);
    maxHeap.push(3);
    maxHeap.push(2);

    // 小根堆
    priority_queue<int, vector<int>, greater<int>> minHeap;
    minHeap.push(1);
    minHeap.push(3);
    minHeap.push(2);

    // 访问大根堆元素
    int maxTop = maxHeap.top();  // maxTop = 3
    maxHeap.pop();  // 之后 top = 2

    // 访问小根堆元素
    int minTop = minHeap.top();  // minTop = 1
    minHeap.pop();  // 之后 top = 2

    // 输出
    cout << "Max Heap Top: " << maxTop << endl;  // Max Heap Top: 3
    cout << "Min Heap Top: " << minTop << endl;  // Min Heap Top: 1

    return 0;
}

运行结果

Max Heap Top: 3
Min Heap Top: 1


5. map

用法小概括:
  • 创建map<key_type, value_type> m;
  • 添加元素m[key] = value;
  • 访问元素m[key] 或 m.at(key)
  • 删除元素m.erase(key);
  • 大小m.size() 返回 size_t
  • 是否为空m.empty() 返回 bool
  • 查找元素m.find(key)
代码实战:
#include <iostream>
#include <map>

using namespace std;

int main() {
    // 创建一个 map
    map<int, string> m;

    // 添加元素
    m[1] = "one";
    m[2] = "two";

    // 访问元素
    string one = m[1];  // one = "one"
    string two = m.at(2);  // two = "two"

    // 删除元素
    m.erase(1);  // 只剩下 {2: "two"}

    // 查找元素
    auto it = m.find(2);  // it != m.end()
    bool found = (it != m.end());

    // 输出
    cout << "One: " << one << endl;  // One: one
    cout << "Two: " << two << endl;  // Two: two
    cout << "Found key 2: " << found << endl;  // Found key 2: 1 (true)

    return 0;
}

运行结果

One: one
Two: two
Found key 2: 1


6. multimap

用法小概括:
  • 创建multimap<key_type, value_type> mm;
  • 添加元素mm.insert({key, value});
  • 访问元素auto range = mm.equal_range(key); (返回一个 pair<iterator, iterator>
  • 删除元素mm.erase(key); 或 mm.erase(iterator);
  • 大小mm.size() 返回 size_t
  • 是否为空mm.empty() 返回 bool
代码实战:
#include <iostream>
#include <map>

using namespace std;

int main() {
    // 创建一个 multimap
    multimap<int, string> mm;

    // 添加元素
    mm.insert({1, "one"});
    mm.insert({1, "uno"});
    mm.insert({2, "two"});

    // 访问元素
    auto range = mm.equal_range(1);
    for (auto it = range.first; it != range.second; ++it) {
        cout << "Key 1: " << it->second << endl;
    }

    // 删除元素
    mm.erase(1);  // 删除所有 key 为 1 的元素

    // 输出
    cout << "Size: " << mm.size() << endl;  // Size: 1 (只剩下 {2: "two"})

    return 0;
}

运行结果

Key 1: one
Key 1: uno
Size: 1


7. string

用法小概括:
  • 创建string str; 或 string str = "text";
  • 访问字符str[index] 或 str.at(index)
  • 追加字符串str.append("more");
  • 连接字符串str + other_str
  • 查找子串str.find("substr") 返回 size_t
  • 子串str.substr(start, length)
代码实战:
#include <iostream>
#include <string>

using namespace std;

int main() {
    // 创建一个 string
    string str = "Hello";

    // 访问字符
    char ch = str[0];  // ch = 'H'
    char ch_at = str.at(1);  // ch_at = 'e'

    // 追加字符串
    str.append(" World");  // str = "Hello World"

    // 连接字符串
    string new_str = str + "!!!";  // new_str = "Hello World!!!"

    // 查找子串
    size_t found = str.find("World");  // found = 6

    // 子串
    string sub = str.substr(6, 5);  // sub = "World"

    // 输出
    cout << "First char: " << ch << endl;  // First char: H
    cout << "Char at index 1: " << ch_at << endl;  // Char at index 1: e
    cout << "New string: " << new_str << endl;  // New string: Hello World!!!
    cout << "Found 'World' at position: " << found << endl;  // Found 'World' at position: 6
    cout << "Substring: " << sub << endl;  // Substring: World

    return 0;
}

运行结果

First char: H
Char at index 1: e
New string: Hello World!!!
Found 'World' at position: 6
Substring: World


8. set

用法小概括:
  • 创建set<type> s;
  • 添加元素s.insert(value);
  • 删除元素s.erase(value);
  • 查找元素s.find(value) 返回 iterator
  • 大小s.size() 返回 size_t
  • 是否为空s.empty() 返回 bool
  • 遍历for (const auto& elem : s) { ... }
代码实战:
#include <iostream>
#include <set>

using namespace std;

int main() {
    // 创建一个 set
    set<int> s;

    // 添加元素
    s.insert(1);
    s.insert(2);
    s.insert(3);

    // 删除元素
    s.erase(2);  // s = {1, 3}

    // 查找元素
    auto it = s.find(3);  // it != s.end()
    bool found = (it != s.end());

    // 输出
    cout << "Found 3: " << found << endl;  // Found 3: 1 (true)
    cout << "Size: " << s.size() << endl;  // Size: 2

    for (const auto& elem : s) {
        cout << elem << " ";  // 输出: 1 3
    }
    cout << endl;

    return 0;
}

运行结果

Found 3: 1
Size: 2
1 3


9. bitset

用法小概括:
  • 创建bitset<size> bs; 或 bitset<size> bs(value);
  • 设置位bs.set(position);
  • 清除位bs.reset(position);
  • 访问位bs[position] 返回 bool
  • 大小bs.size() 返回 size_t
  • 位数bs.count() 返回 size_t
代码实战:
#include <iostream>
#include <bitset>

using namespace std;

int main() {
    // 创建一个 bitset
    bitset<8> bs;

    // 设置位
    bs.set(1);  // bs = 00000010
    bs.set(3);  // bs = 00001010

    // 清除位
    bs.reset(1);  // bs = 00001000

    // 访问位
    bool bit1 = bs[1];  // bit1 = 0
    bool bit3 = bs[3];  // bit3 = 1

    // 位数
    size_t count = bs.count();  // count = 1

    // 输出
    cout << "Bit 1: " << bit1 << endl;  // Bit 1: 0
    cout << "Bit 3: " << bit3 << endl;  // Bit 3: 1
    cout << "Count of set bits: " << count << endl;  // Count of set bits: 1
    cout << "Bitset: " << bs << endl;  // Bitset: 00001000

    return 0;
}

运行结果

Bit 1: 0
Bit 3: 1
Count of set bits: 1
Bitset: 00001000


10. 哈希表(unordered_map

用法小概括:
  • 创建unordered_map<key_type, value_type> um;
  • 添加元素um[key] = value;
  • 访问元素um[key] 或 um.at(key)
  • 删除元素um.erase(key);
  • 大小um.size() 返回 size_t
  • 是否为空um.empty() 返回 bool
  • 查找元素um.find(key) 返回 iterator
代码实战:
#include <iostream>
#include <unordered_map>

using namespace std;

int main() {
    // 创建一个 unordered_map
    unordered_map<int, string> um;

    // 添加元素
    um[1] = "one";
    um[2] = "two";

    // 访问元素
    string one = um[1];  // one = "one"
    string two = um.at(2);  // two = "two"

    // 删除元素
    um.erase(1);  // 只剩下 {2: "two"}

    // 查找元素
    auto it = um.find(2);  // it != um.end()
    bool found = (it != um.end());

    // 输出
    cout << "One: " << one << endl;  // One: one
    cout << "Two: " << two << endl;  // Two: two
    cout << "Found key 2: " << found << endl;  // Found key 2: 1 (true)
    cout << "Size: " << um.size() << endl;  // Size: 1

    return 0;
}

运行结果

One: one
Two: two
Found key 2: 1
Size: 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

背水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值