基础数据结构 学习笔记
1. 栈
1.1 栈的性质
后进先出。
1.2 栈处理括号序列
一个栈,遇到左括号压入栈,遇到右括号判断栈顶是否为左括号,如果是则弹出栈顶,否则不合法。
bool flag=1;
for(int i=0; i<s.size(); i++) {
if(s[i]=='(') st.push(s[i]);
else if(st.top()=='(') st.pop();
else flag=0;
}
if(st.size()) flag=0;
puts(flag?"YES":"NO");
return 0;
1.3 单调栈
单调队列平替。例如维护一个顶小的单调栈,插入 \(x\) 时先将 \(<x\) 的弹出,然后插入。常用于优化 dp。
2. 队列
2.1 队列的性质
先进先出。
2.2 单调队列
维护一个头小尾大的单调队列,插入 \(x\) 时从队末将 \(>x\) 的弹出,然后插入。常用于优化 dp。
int head=1,tail=0;
//in a[x]
while(head<=tail && a[q[tail]]>a[x]) tail--;
q[++tail]=x;
while(head<=tail && x-q[head]>=t/*不满足条件*/) head++;
3. 并查集
值得注意的是路径压缩会破坏树的形态。
struct easy_dsu {
int fa[maxn];
int find(int x) {
while(fa[x]^x) x=fa[x]=fa[fa[x]];
return x;
}
void init(int n) {
for(int i=1; i<=n; i++) fa[i]=i;
}
void merge(int x,int y) {
int rx=find(x),ry=find(y);
fa[rx]=ry;
}
bool query(int x,int y) {
return find(x)==find(y);
}
};
struct size_dsu {
int fa[maxn],sz[maxn];
int find(int x) {
while(fa[x]^x) x=fa[x];
return x;
}
void init(int n) {
for(int i=1; i<=n; i++) fa[i]=i,sz[i]=1;
}
void merge(int x,int y) {
int rx=find(x),ry=find(y);
sz[ry]+=rx;
fa[rx]=ry;
}
bool query(int x,int y) {
return find(x)==find(y);
}
};
struct dis_dsu {
int fa[maxn],dis[maxn];
int find(int x) {
if(fa[x]!=x) {
int y=find(fa[x]);
dis[x]+=dis[fa[x]];
fa[x]=y;
}
return fa[x];
}
void init(int n) {
for(int i=1; i<=n; i++) fa[i]=i,dis[i]=0;
}
void merge(int x,int y) {
int rx=find(x),ry=find(y);
fa[rx]=ry;
}
bool query(int x,int y) {
return find(x)==find(y);
}
};
struct merge_by_rank {
//不会破坏树的形态
int fa[maxn],rk[maxn];
int find(int x) {
while(fa[x]^x) x=fa[x];
return x;
}
void init(int n) {
for(int i=1; i<=n; i++) fa[i]=i,rk[i]=0;
}
void merge(int x,int y) {
int rx=find(x),ry=find(y);
if(rx^ry) {
if(rk[rx]<rk[ry]) fa[rx]=ry;
else if(rk[ry]<rk[rx]) fa[ry]=rx;
else fa[rx]=ry,rk[ry]++;
}
}
bool query(int x,int y) {
return find(x)==find(y);
}
};
4. hash 表
给一个数处理一个 hash 值,有奇妙的用处。
考虑如果一段区间合法,那么你对每个数值进行哈希,这段区间哈希值的和必定是 \(k\) 的倍数。注意到这只是充分条件,不能反推,所以多哈希几次就行。
对每个数hash一下,暴力匹配。
5. 链表
实现相邻访问 \(O(1)\),快速从头尾删除、插入,快速随机访问。
6. ST表
一个很像 dp 的数据结构,可以做到 \(O(n\log n)-O(1)\) 维护区间最大值、最小值、gcd等可重复性的信息。
以P3865 为例。
设 \(f_{i,j}\) 表示从 \(i\) 开始 \(2^j\) 个位置的最大值。
那么可以得出状态转移方程 \(f_{i,j}=\max(f_{i,j-1},f_{i+2^j-1,j-1})\)。
那么可以 \(O(n\log n)\) 转移。
考虑查询:可以直接查询 \([x,x+2^k]\) 和 \([y-2^k+1,y]\),其中 \(k=\log_2(y-x+1)\)。
最小值同理。
同样,我们也可以维护区间 gcd,因为 gcd 重复算肯定是可以的。P1890