单链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int e[maxn],ne[maxn];int head,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]]; }
双链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int idx;int e[maxn],l[maxn],r[maxn];void init () { r[0 ]=1 ; l[1 ]=0 ; idx=2 ; } void add (int k,int x) { e[idx]=x; r[idx]=r[k]; l[idx]=k; l[r[k]]=idx; r[k]=idx; idx++; } void del (int k) { r[l[k]]=r[k]; l[r[k]]=l[k;] }
调用函数
1 2 add (r[0 ],x);add (l[1 ],x);
模拟栈 栈顶插入 栈顶弹出 判断是否为空 查询栈顶元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int stk[maxn];int tt=0 ;void push (int x) { stk[++tt]=x; } void pop () { --tt; } bool empty () { return tt>0 ?false :true ; } int query () { return stk[tt]; }
模拟队列 队尾插入 队头弹出 判断是否为空 查询队头元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int q[maxn];int tt=-1 ,hh=0 ;void push (int x) { q[++tt]=x; } void pop () { hh++; } bool empty () { return hh>tt?false :true ; } int query () { return q[hh]; }
单调栈 输出左侧第一个比当前数字小的数 没有输出-1
1 2 3 4 const int maxn=1e5 +10 ;int stk[maxn];int tt=0 ;int n,x;
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i=0 ;i<n;i++){ cin>>x; while (tt&&stk[tt]>=x){ tt- -; } if (tt){ cout<<stk[tt]<<" " ; } else { cout<<"-1 " ; } stk[++tt]=x; }
单调队列 有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边且每次向右移动一个位置,分别输出窗口的最大值和最小值. 初始化并读入到a[]中
1 2 3 4 5 6 7 const int maxn=1e6 +10 ;int a[maxn],q[maxn];int tt=-1 ,hh=0 ;for (int i=0 ;i<n;i++){ cin>>a[i]; }
求最小值
1 2 3 4 5 6 7 8 9 10 11 12 for (int i=0 ;i<n;i++){ if (hh<=tt&&i-q[hh]+1 >k){ hh++; } while (hh<=tt&&a[q[tt]]>=a[i]){ tt--; } q[++tt]=i; if (i>=k-1 ){ cout<<a[q[hh]]<<" " ; } }
求最大值
1 2 3 4 5 6 7 8 9 10 11 12 for (int i=0 ;i<n;i++){ if (hh<=tt&&i-q[hh]+1 >k){ hh++; } while (hh<=tt&&a[q[tt]]<=a[i]){ tt--; } q[++tt]=i; if (i>=k-1 ){ cout<<a[q[hh]]<<" " ; } }
KMP字符串匹配 初始化并输入
1 2 3 4 5 6 const int N=1e5 +10 ,M=1e6 +10 ;char p[N],s[M];int ne[N];int n,m;cin>>n>>p+1 >>m>>s+1 ;
求next[]
1 2 3 4 5 6 7 8 9 10 ne[1 ]=0 ; for (int i=2 ,j=0 ;p[i];i++){ while (j&&p[i]!=p[j+1 ]){ j=ne[j]; } if (p[i]==p[j+1 ]){ j++; } ne[i]=j; }
KMP算法
1 2 3 4 5 6 7 8 9 10 11 12 for (int i=1 ,j=0 ;s[i];i++){ while (j&&s[i]!=p[j+1 ]){ j=ne[j]; } if (s[i]==p[j+1 ]){ j++; } if (j==n){ cout<<i-n<<" " ; j=ne[j]; } }
Trie字符串统计 I x 向集合中插入一个字符串 x Q x 询问一个字符串在集合中出现了多少次 初始化
1 2 3 4 const int maxn=1e5 +10 ;int son[maxn][26 ],cnt[maxn];int idx=0 ;char op[2 ];
插入操作
1 2 3 4 5 6 7 8 9 10 11 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]++; }
查找操作
1 2 3 4 5 6 7 8 9 10 int query (char str[]) { int p=0 ; for (int i=0 ;str[i];i++){ int u=str[i]-'a' ; if (!son[p][u]){ return 0 ; } } return cnt[p]; }
主函数
1 2 3 4 5 6 7 8 9 10 11 int n;cin>>n; while (n--){ cin>>op>>str; if (op[0 ]=='I' ){ insert (str); } if (op[0 ]=='Q' ){ cout<<query (str)<<endl; } }
并查集 ①讲两个集合合并 ②询问两个集合是否在一个集合当中
基本原理:每个集合用一棵树来表示.树根的标号就是整个集合的编号.每个节点存储它的父节点.p[x]表示 x 的父节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 for (int i=1 ;i<=n;i++){ fa[i]=i; } if (p[x]==x){} while (p[x]!=x){ x=p[x]; } int find (int x) { if (p[x]!=x){ p[x]=find (p[x]); } return p[x]; } px=find (x); py=find (y); p[px]=py;
模拟堆 ① I x 插入一个数 x; ② PM 输出当前集合中的最小值;(输出堆顶 h[1]) ③ DM 删除当前集合中的最小值(数据保证此时的最小值唯一); ④ D k 删除第 k 个插入的数; ⑤ C k x 修改第 k 个插入的数,将其变为 x;
预处理
1 2 3 4 5 6 7 int n;int siz;int m;int k,x;int h[N];int ph[N],hp[N];
1 2 3 4 5 void heap_swap (int a,int b) { swap (ph[hp[a]],ph[hp[b]]); swap (hp[a],hp[b]); swap (h[a],h[b]); }
两个堆的排序函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void down (int u) { int t=u; if (u*2 <=siz&&h[u*2 ]>h[t]){ t=u*2 ; } if (u*2 +1 <=siz&&h[u*2 +1 ]>h[t]){ t=u*2 +1 ; } if (t!=u){ heap_swap (u,t); down (t); } return ; } void up (int u) { while (u/2 >=1 &&h[u/2 ]>h[u]){ heap_swap (u,u/2 ); u/=2 ; } return ; }
操作实现
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 if (!strcmp (op,"I" )){ cin>>x; m++;siz++; ph[m]=siz;hp[siz]=m; h[siz]=x; up (siz); } else if (!strcmp (op,"DM" )){ heap_swap (1 ,siz); siz--; up (1 ); down (1 ); } else if (!strcmp (op,"D" )){ cin>>k; k=ph[k]; heap_swap (k,siz); siz--; up (k); down (k); } else if (!strcmp (op,"C" )){ cin>>k>>x; k=ph[k]; h[k]=x; up (k); down (k); }
哈希表 开放寻址法(蹲坑法) 1 2 3 const int null=0x3f3f3f3f ;const int N=2e5 +3 ;int h[N];
1 2 3 4 5 6 7 8 9 10 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; }
1 2 3 4 5 6 7 8 9 10 11 12 memset (h,0x3f ,sizeof (h));int k=find (x);h[k]=x; int k=find (x);if (h[k]!=null){ puts ("Yes" ); } else { puts ("No" ); }
拉链法 1 2 3 const int N=2e5 +3 ;int h[N];int e[N],ne[N],idx;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void insert (int x) { int k=(x%N+N)%N; e[idx]=x; ne[idx]=k; h[k]=idx; idx++; } bool find (int x) { int k=(x%N+N)%N; for (int i=h[k];i!=-1 ;i=ne[i]){ if (e[k]==x){ return true ; } } return false ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 memset (h,-1 ,sizeof (h));if (*op=='I' ){ cin>>x; insert (x); } else if (*op=='Q' ){ cin>>x; if (find (x)){ puts ("Yes" ); } else { puts ("No" ); } } }
字符串哈希 判断一个字符串的两个子区间是否完全相等
1 2 3 4 5 6 7 typedef unsigned long long ULL;const int N=1e5 +5 ,Q=133 ;char str[N];ULL p[N]; ULL h[N]; int n,m;int l1,r1,l2,r2,
1 2 3 ULL get (int l,int r) { return h[r]-h[l-1 ]*p[r-l+1 ]; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cin>>n>>m; p[0 ]=1 ; for (int i=1 ;i<=n;i++){ p[i]=p[i-1 ]*Q; h[i]=h[i-1 ]*Q+str[i]; } while (m--){ cin>>l1>>r1>>l2>>r2; if (get (l1,r1)==get (l2,r2)){ puts ("Yes" ); } else { puts ("No" ); } }