- 主要内容
- 链表
- 哈希表
- 并查集:在近乎O(1)的情况下,
- 关键:p[N]初始化,find(int a):返回a所在集合的编号
- 合并 两集合
- 询问 两元素是否在同一集合
链表与邻接表
数组模拟单链表(邻接表)
AcWing826 单链表(模板题)
- 一般来说,如果想删除链表第一个节点,会说“删除头结点”,如果想删除整个链表,会直接说“删除整个链表”~ head是指向头结点的指针,它本身是不存节点的,只是指向了整个链表的第一个节点。
- 初始化:head=-1;idx=0;
- 头插法:赋值、指向当前head的next、更新head,idx++
- 插到指定位置k后面:赋值,更新next
- 删除位置k的元素:将k的next指向k的next的next
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include<bits/stdc++.h> using namespace std; const int N=100010;
int head,e[N],ne[N],idx;
void init(){ head=-1; idx=0; }
void add_to_head(int x){ e[idx]=x,ne[idx]=head,head=idx,idx++; }
void add(int k,int x){ e[idx]=x,ne[idx]=ne[k],ne[k]=idx,idx++; }
void remove(int k){ ne[k]=ne[ne[k]]; }
int main(){ ios::sync_with_stdio(false); int m; cin>>m; init(); while(m--){ int k,x; char op; cin>>op; if(op=='H'){ cin>>x;add_to_head(x); }else if(op=='D'){ cin>>k; if(k==0) head=ne[head]; else remove(k-1); }else{ cin>>k>>x; add(k-1,x); } } for(int i=head;i!=-1;i=ne[i]) cout<<e[i]<<' '; }
|
数组模拟双链表
827. 双链表 - AcWing题库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include<bits/stdc++.h> using namespace std; const int M=1e5+10; int l[M],r[M],e[M],idx; void init(){ r[0]=1;l[1]=0;idx=2; } void insert(int k,int v){ e[idx]=v; l[idx]=k,r[idx]=r[k]; l[r[k]]=idx,r[k]=idx; idx++; } void remove(int k){ l[r[k]]=l[k]; r[l[k]]=r[k]; } int main(){ int m;cin>>m; init(); while(m--){ string s;int k,x; cin>>s; if(s=="L"){ cin>>x; insert(0,x); }else if(s=="R"){ cin>>x; insert(l[1],x); }else if(s=="D"){ cin>>k; remove(k+1); }else if(s=="IL"){ cin>>k>>x; insert(l[k+1],x); }else{ cin>>k>>x; insert(k+1,x); } } for(int i=r[0];i!=1;i=r[i]) cout<<e[i]<<' '; }
|
栈和队列
数组模拟栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std; const int N = 100010; int st[N], top = -1; int n; int main(){ cin >> n; while(n--){ string s; cin >> s; if(s == "push"){ int a; cin >> a; st[++top] = a; } if(s == "pop") top --; if(s == "query") cout << st[top] << endl; if(s == "empty") cout << (top == -1 ? "YES" : "NO") << endl; } }
|
数组模拟队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> using namespace std; const int N = 100010; int m; int q[N], hh, tt = -1; int main(){ cin >> m; while (m -- ){ string op; int x; cin >> op; if (op == "push"){ cin >> x; q[ ++ tt] = x; } else if (op == "pop") hh ++ ; else if (op == "empty") cout << (hh <= tt ? "NO" : "YES") << endl; else cout << q[hh] << endl; } return 0; }
|
单调栈
830. 单调栈 - AcWing题库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std; const int N=1e5+10; stack<int>stk; int main(){ int n;cin>>n; while(n--){ int x;cin>>x; while(stk.size()&&stk.top()>=x) stk.pop(); if(!stk.size()) cout<<"-1"<<' '; else{ cout<<stk.top()<<' '; } stk.push(x); } }
|
单调队列
求滑动窗口的max、min
154. 滑动窗口 - AcWing题库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include<bits/stdc++.h> using namespace std; const int N=1e6+10; int a[N],q[N];
int hh,tt=-1; int main(){ cin.tie(0); ios::sync_with_stdio(false); int n,k;cin>>n>>k; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++){ if(hh<=tt&&i-k+1>q[hh]) hh++; while(hh<=tt&&a[q[tt]]>=a[i]) tt--; q[++tt]=i; if(i>=k-1) cout<<a[q[hh]]<<' '; } cout<<endl; hh=0,tt=-1; for(int i=0;i<n;i++){ if(hh<=tt&&i-k+1>q[hh]) hh++; while(hh<=tt&&a[q[tt]]<=a[i]) tt--; q[++tt]=i; if(i>=k-1) cout<<a[q[hh]]<<' '; } }
|
KMP
哈希表
用到哈希表的情况:把一个大的空间映射到一个小的空间
基础知识
- 存储结构
- 开放寻址法
- 拉链法:开一个一维数组,存储所有的哈希值
- 字符串哈希方式
离散化可以看成是一种特殊的哈希,之前的那个离散化要保证顺序
AcWing 840 模拟散列表
题意
维护一个集合,支持下列操作:
- lx: 插入一个数
- Qx:询问x是否在集合中出现过
对于N次操作,输出询问结果
思路
N行:N个操作,虽然-10^9 <= x <= 10^9
但是,N最大取到10^5,所以实际只涉及10^5个数;
题目操作只有插入和查询而已,没有说要插入到哪个位置,而只是插入到了一个无序的集合中,并且没有顺序要求。
对于插入操作,我们可以把要插入的每个数存到一个哈希表里里面
x mod 10^5
- 一般来说,取模的这个数要是质数,且离2的整数次幂远
- 因此确定数组大小后,首先找一个大于最大数据范围的一个质数
1 2 3 4 5 6 7 8 9 10 11 12
| for(int i=dest;;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; } }
|
代码(拉链法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<bits/stdc++.h> using namespace std; const int N=100003; 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; } int main(){ 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"); } } }
|
代码(开放寻址法比较nice)
开放寻址法:只开一个一维数组,开到题目数据范围的2-3倍(首先要找到这个大于最大数据范围的一个质数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<bits/stdc++.h> using namespace std; const int N=200003,null=0x3f3f3f3f; int h[N]; int find(int x){ int k=(x%N+N)%N; while(h[k]!=null&&h[k]!=x){ k++; if(k==N) k=0; } return k; } int main(){ int n; scanf("%d",&n); memset(h,0x3f,sizeof h); while(n--){ char op[2];int x; scanf("%s%d",op,&x); int k=find(x); if(*op=='I'){ h[k]=x; }else{ if(h[k]!=null) puts("Yes"); else puts("No"); } } }
|