一些基础数据结构
一些基础数据结构
Trie树
高效地存储和查找字符串集合的数据结构
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int son[N][26],cnt[N],idx;//下标是0的点既是根节点又是空节点
char str[N];
void insert(char str[])
{
int p=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';//将字母映射到下标上
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++)
{
int u=str[i]-'a';
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 cout<<query(str)<<endl;
}
return 0;
}
并查集
C语言朴素代码
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
int uset[MAXSIZE];//定义一个足够长的数组
//用下标来表示结点
/*
构造并查集
int size 有多少个节点
*/
void makeSet(int size)
{
for (int i = 0; i < size; i++)
{
//各自为各自的代表
uset[i] = i;
}
}
/*
找到元素所在的集合的代表 如果位于同一集合
*/
int find(int i)
{
if (i == uset[i])
{
return i;
}
return find(uset[i]);
}
void unite(int x, int y)
{
//先找相对应的代表
int x = find(x);
int y = find(y);
if (x == y)
{
return;
}
uset[x] = y;
}
C语言路径压缩
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
/*
因为在特殊情况下 这棵树可能是一个长长的树链 设链的最后一个节点为x
每次执行find 相当于遍历整个链条
只需要把便利过的结点都改成跟的子节点 查询就会快很多
*/
int uset[MAXSIZE];//定义一个足够长的数组 每个结点
int rank[MAXSIZE];//树的高度
//用下标来表示结点
/*
构造并查集
int size 有多少个节点
*/
void makeSet(int size)
{
for (int i = 0; i < size; i++)
{
//各自为各自的代表
uset[i] = i;
//树的高度
rank[i] = 0;
}
}
/*
找到元素所在的集合的代表 如果位于同一集合
查找元素在哪个集合
*/
int find(int i)
{
if (i == uset[i])
{
return i;
}
return uset[i] = find(uset[i]);//在第一次查找的时候 将结点直接连到跟
}
void unite(int x, int y)
{
//先找相对应的代表
int x = find(x);
int y = find(y);
if (x == y)
{
return;
}
//判断两棵树的高度 在决定谁为子树
if (rank[x] < rank[y])
{
uset[x] = y;
}
else
{
uset[y] = x;
if (rank[x] == rank[y])
{
rank[x]++;
}
}
}
堆排序
- 插入一个数
heap[++size]=x;up(size); - 求集合中的最小值
heap[1]; - 删除最小值
heap[1]=heap[size];size--;down(1) - 删除任意一个元素
heap[k]=heap[size];size--;down(k);up(k); - 修改任意一个元素
heap[k]=x;down(k);up(k);
存储方式:完全二叉数(一维数组)左儿子2x,右儿子2x+1
性质:根是最小值
操作:down和up
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 100010;
int h[N], ph[N], hp[N], cnt;
void heap_swap(int a, int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a], hp[b]);
swap(h[a], h[b]);
}
//down:将大数下压
void down(int u)
{
int t = u;
if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t)
{
heap_swap(u, t);
down(t);
}
}
//up:将小数上移
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
heap_swap(u, u / 2);
u >>= 1;
}
}
int main()
{
int n, m = 0;
scanf("%d", &n);
while (n -- )
{
char op[5];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I"))
{
scanf("%d", &x);
cnt ++ ;
m ++ ;
ph[m] = cnt, hp[cnt] = m;
h[cnt] = x;
up(cnt);
}
else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
else if (!strcmp(op, "DM"))
{
heap_swap(1, cnt);
cnt -- ;
down(1);
}
else if (!strcmp(op, "D"))
{
scanf("%d", &k);
k = ph[k];
heap_swap(k, cnt);
cnt -- ;
up(k);
down(k);
}
else
{
scanf("%d%d", &k, &x);
k = ph[k];
h[k] = x;
up(k);
down(k);
}
}
return 0;
}
哈希表
哈希表的存储结构
哈希函数:h(x)=x%1e5
哈希冲突:拉链法
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+3;
int h[maxn],e[maxn],idx,ne[maxn];
void insert(int x)
{
int k=(x%maxn+maxn)%maxn;
e[idx]=x;
ne[idx]=h[k];
h[k]=idx++;
}
bool find(int x)
{
int k=(x%maxn+maxn)%maxn;
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);
string op;int x;
while(n--)
{
cin>>op;
cin>>x;
if(op[0]=='I')insert(x);
else
{
if(find(x))puts("Yes");
else puts("No");
}
}
return 0;
}
开放寻址法
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+3,null=0x3f3f3f3f;
int h[maxn];
int find(int x)//返回这个值的位置或者可以存放的位置
{
int t=(x%maxn+maxn)%maxn;
while(h[t]!=null&&h[t]!=x)
{
t++;
if(t==maxn)t=0;
}
return t;
}
int main()
{
int n;cin>>n;
memset(h,null,sizeof h);
string op;int x;
while(n--)
{
cin>>op>>x;
if(op[0]=='I')h[find(x)]=x;
else
{
if(h[find(x)]==null)puts("No");
else puts("");
}
}
return 0;
}
字符串前缀哈希
- 不用0当哈希值
- 完全不考虑冲突
- 经验值:p=131,q=2^60
- 实现原理
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int maxn=1e5+10,P=131;
int n,m;
char str[maxn];
ULL h[maxn],p[maxn];
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
cin>>n>>m;
cin>>str+1;
p[0]=1;
for(int i=1;i<=n;i++)
{
h[i]=h[i-1]*P+str[i];
p[i]=p[i-1]*P;
}
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
变长数组,倍增的思想
vector<int>a(10,3);//定义一个长度为10的vector并初始化为3
a.size();
a.empty();
a.clear();
a.front();a.back();
a.push_back();a.pop_back();
a.begin();a.end();
a[idx];
//为程序分配空间时,所需时间与空间大小无关,只与申请次数有关
//迭代方式
for(int i=0;i<a.size();i++)cout<<a[i]<<" ";
for(vector<int>::iterator i=a.begin();i!=a.end();i++)cout<<*i<<" ";
for(auto x:a)cout<<x<<" ";
//支持比较运算 按字典序
pair
pair<int,string>p;
p.first;p.second;
//支持比较运算,按照字典序,以first为优先级
//初始化
p=make_pair(10,"jhs");
p={20,"jhs"};
pair<int,pair<int,int>>p;
string
字符串
string a="yxc";
a+="def";a+='c';
a.substr(1,2);//返回从下标1开始长度为2的子串
size();empty();clear();
a.c_str();//返回字符数组的起始地址
queue/priority_queue
queue<int>q;
q.size();q.empty();q.push(x);q.front();q.back();q.pop();
//默认大根堆
priority_queue<int>heap;
q.push();q.top();q.pop;
//定义小根堆
q.push(-x);
priority_queue<int,vector<int>,greater<int>>heap;
stack
stack<int>s;
s.push();s.top();s.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();++;--;//返回前驱和后继
//set/mulitset
insert();//插入一个数
find();//查找一个数,返回end的迭代器
count();//返回某一个数的个数
erase();//删除迭代器或者数字
lower_bound(x);//返回大于等于x的最小的数
upper_bound(x);//返回大于x的最小的数
//都是返回迭代器,找不到则返回end
//map/multimap
insert();//插入的数是一个pair
erase();//删除迭代器或者pair
find();
[];//时间复杂度logn
unordered_set/unordered_map/underordered_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位取反
归并排序
模板:
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int q[N],temp[N];//存原数组和中间变量数组
void merge_sort(int q[],int l,int r)
{
if(l>=r)return;
int mid=l+r>>1;
merge_sort(q,l,mid);
merge_sort(q,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<=q[j])temp[k++]=q[i++];
else temp[k++]=q[j++];
}
while(i<=mid)temp[k++]=q[i++];
while(j<=r)temp[k++]=q[j++];
for(i=l,j=0;i<=r;i++,j++)q[i]=temp[j];
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",%q[i]);
merge_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d ",q[i]);
return 0;
}
例题:求逆序对的数量:
class Solution {
public:
int merge(vector<int> &nums,int l,int r){
if(l>=r)return 0;//当只有一个元素
int mid=(l+r)>>1;
int res=merge(nums,l,mid)+merge(nums,mid+1,r);//两边的逆序对数量
int i=l,j=mid+1;//因为是向下取整,所以要加一
vector<int>temp;
while(i<=mid&&j<=r){
if(nums[i]<=nums[j])temp.push_back(nums[i++]);
else{
temp.push_back(nums[j++]);
res+=mid+1-i;//这里是推导式,当右边的数字归并时,在其前面比其大的数字个数就是mid-1+i-i
}
}
while(i<=mid)temp.push_back(nums[i++]);
while(j<=r)temp.push_back(nums[j++]);
i=l;
for(auto x:temp)nums[i++]=x;//见归并后的结果填入数组
return res;
}
int inversePairs(vector<int>& nums) {
return merge(nums,0,nums.size()-1);
}
};

浙公网安备 33010602011771号