7.5
字典树
高效存储和查找字符串的一种数据结构
使用数组存储树
#include<iostream>
using namespace std;
const int N=100010;
int son[N][26],cnt[N],idx;
char str[N];
void insert(char str[]){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'0';
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
int query(char str[]){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'0';
if(!son[p][u]) return 0;
p=son[p][u];
}
return cnt[p];
}
int main(){
int n;
cin>>n;
while(n--){
char op[2];
scanf("%s%s",op,str);
if(op[0]=='I') insert(str);
else printf("%d\n",query(str));
}
return 0;
}
解释
son[i][26]表示一个节点可能出现的26个后继
idx效果与单链表中的idx相同,可以直接对应到一个特定字符串。cnt[idx]就可以索引对应字符串的数目。
并查集
处理的操作有:
1.将两个集合合并
2.询问两个元素是否在同一个集合中
把根节点作为集合的标号,所有的节点都归属于根节点。
路径压缩,这里跟图论里面的kruscal算法里面使用的连通集相似。
#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int p[N];
int find(int x){ //返回祖宗节点并进行路径压缩
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) p[i]=i;
while(m--){
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0]=='M') p[find(a)]=find(b);
else{
if(find(a)==find(b)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
return 0;
}
统计连通元素的个数
使用一个size[]数组,在合并之前加给新的根节点。
s[find(b)]+=s[find(a)];
7.6
堆
堆是完全二叉树的形式。
堆的存储方式是我们在学二叉树的时候就接触过的。
主要靠两个函数:up()、down()来动态维护。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int n,m;
int h[N],s;
void down(int u){
int t=u;
if(u*2<=s&&h[u*2]<h[t]) t=u*2;
if(u*2+1<=s&&h[u*2+1]<h[t]) t=u*2+1;
if(u!=t){
swap(h[u],h[t]);
down(t);
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&h[i]);
s=n;
for(int i=n/2;i;i--) down(i);
while(m--){
cout<<h[1]<<" ";
h[1]=h[s--];
down(1);
}
return 0;
}