AcWing——数据结构3

哈希表

①存储结构:开放寻址法、拉链法

②字符串哈希方式

一般是通过取模,Mod 一个质数(且尽量离2的整数幂远)  可能发生冲突,所以用开放寻址法、拉链法

对于拉链法,在每个槽上拉一条链,冲突就在链上加

对于开放寻址法,只开了一个一维数组,范围在题目范围的2-3倍。开到一个位置,如果这个槽有数就下一个,直到找到第一个空的位置。

对于字符串哈希,如STR:“ABCABC../”,则h[0]=0,h[1]="A",h[2]="AB",h[3]="ABC"...

ABCD->(1,2,3,4)p=(1*p3+2*p2+3*p1+4*p0) mod Q

Tip:不能映射成0

若不存在冲突,对于P的经验值是131或13331,则Q取2 的16次方

对于区间【1~L~R】,已知1-L,1-R的哈希,求L~R的哈希:

左边是高位,右边是低位,那么1处是R-1,R处是0

公式是h[R]-h[L]*P^(R-L+1)

 核心思想:将字符串看成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 - 1];
    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];
}

1.模拟散列表

 ①拉链法

//拉链法,其实本质上就是N个单链表,就是头结点head变成了h[i] 
#include <iostream>
#include <cstring>

using namespace std;

const int N = 100003;  // 取大于1e5的第一个质数,取质数冲突的概率最小

//h是一个数组,对应索引上面连着一个链表,h[i]的值是i索引上的链表中的头结点的下一个元素的指针,头结点是留空的; h[N]是槽,e[N]是链表的值,ne[N]是下一个的位置
int h[N], e[N], ne[N], idx;  

//插入的方式是往头结点插入,具体可以看单链表 
void insert(int x) {
    int k = (x % N + N) % N;//+N,%N 是防止x是负数 
    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;
}


int main() {
	int n; 
    cin >> n;
	memset(h, -1, sizeof h);  //将h数组全部元素的值赋为-1
    while (n--) {
        string op;
        int x;
        cin >> op >> x;
        if (op == "I") insert(x);
        else {
            if (find(x)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}

 ②开放寻址法

//开放寻址法 

#include <cstring>
#include <iostream>

using namespace std;

//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了
const int N = 200003;        //大于数据范围的第一个质数
const int null = 0x3f3f3f3f;  //规定空指针为 null 0x3f3f3f3f

int h[N];

//插入与查找合为一体的函数 
int find(int x) {
    int k = (x % N + N) % N;
    //h[k] != null是插入操作的条件,h[k] != x是查找操作的条件
	//跳出循环的条件h[k] == null既是插入位置的最终结果,也是未找到待查找元素的最终结果
    //这里可能会考虑用for循环代码更短,但提交可能会超时,采用while循环会更快
    while ( h[k] != null && h[k] != x ) {
        k++;
        if (k == N) k = 0;
    }
    return k;  
}



int main() {
	int n; 
    cin >> n;
    
    memset(h, 0x3f, sizeof h);  //规定空指针为 0x3f3f3f3f

    while (n--) {
        string op;
        int x;
        cin >> op >> x;
        if (op == "I") {
            h[find(x)] = x;
        } else {
            if (h[find(x)] == x) {
                puts("Yes");
            } else {
                puts("No");
            }
        }
    }
    return 0;
}

2.字符串哈希

#include <iostream>

using namespace std;

const int N = 100010,P = 131;//P=131 或 P=13331

typedef unsigned long long ull;//因为Q是2的64次方

char str[N];

//p[i]是131的i次方,主要就是用到一个p[r - l + 1];h[i]是指前i个字母的哈希值,p和h下标都从1开始考虑 
ull h[N],p[N];

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

int main(){
	int n,m;
	cin>>n>>m>>str;
	p[0] = 1;
    //初始化
	for(int i = 1; i <= n; i++){
		p[i] = p[i-1] * P;
		h[i] = h[i-1] * P + str[i - 1];//计算字符串哈希值的公式
		
	}
	while(m--){
		int l1,r1,l2,r2;
		cin>>l1>>r1>>l2>>r2;
		if(get(l1,r1) == get(l2,r2)) puts("Yes");
		else puts("No");
	}
	return 0;
} 

STL 

vector, 变长数组,倍增的思想,自动变长

系统为每一程序分配空间时候,所需的时间与空间大小无关。与申请次数有关,所以变长数组要减少申请次数,每次数组长度不够就将长度*2

最终申请10^6时,1+2+4+...+5*10^5=10^6
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back() 返回第一个数/最后一个数
    push_back()/pop_back()  在最后插入一个数/把最后一个数删掉
    begin()/end() 第0个数/最后一个数的后一个数
    [] 
 

在前面#include <vector>

定义:vector<int> a;

插入:for(int i =0 ;i <10 ;i++) a.push_back(i)

遍历:for(int i = 0;i<a.size(); i++) cout << a[i] <<' '; cout end1;//数组下标遍历

for (vector<int>:: itreator  i=i.begin();  i != a.end(); i++) cout <<   *i << ' '; cout << end1;//迭代器遍历

for (auto x :a ) cout << x << ' '; cout << end1; //范围遍历,效率快一些

支持比较运算,按字典序

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

初始化:pair<int,string>p;

p = make_pair(10,"yxc"); /p =(20,"abc");

存三个不同东西:pair<int,pair<int,int>>p;

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

string a ="sss";

a+="def";//可以在后面加

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

想清空直接重新创建就行,没有clear

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

定义:priority_queue<int> heap;

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不支持此操作。 时  map/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位取反

 

ACwing是一个在线的程序设计竞赛训练平台,提供了丰富的算法题目和解题思路。在ACwing上,数据结构是其中一个重要的学习内容。根据引用内容,我们可以得出以下观点。 首先,数据结构在解决问题时起到了至关重要的作用。STL(Standard Template Library,标准模板库)是一种常用的C++库,其中包含了各种数据结构和算法。然而,STL不一定能够满足所有的需求,有些问题可能需要使用数组来解决。因此,学习数组的方法对于实现各种数据结构是非常重要的。 其次,使用结构体和指针来创建数据结构节点时,每次创建一个新节点都需要执行new Node()操作,这个操作可能会比较慢。特别是在处理大量数据的情况下,频繁的new操作可能导致运行时间超时。因此,在设计数据结构时需要考虑到运行效率的问题。 最后,在比赛中,通常没有进行O2优化。在这种情况下,纯STL可能会比数组模拟的数据结构稍慢一些。这是因为STL包含了更多的细节和抽象,而数组模拟的数据结构更为直接和简单。然而,在实际应用中,选择使用哪种数据结构还是要根据具体问题的需求和性能要求来决定。 综上所述,ACwing数据结构包含了使用STL和数组等不同的方法来实现各种数据结构。根据具体问题的需求和性能要求,选择合适的数据结构方法是很重要的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【AcWing 算法基础课】 2、数据结构 笔记](https://blog.youkuaiyun.com/qq_40915831/article/details/124761243)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值