链表与邻接表:树与图的存储
栈与队列:单调队列、单调栈
kmp
Trie
并查集
堆
Hash表
单链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int head, e[N], ne[N], idx;void init () { head = -1 ; idx = 0 ; } void insert (int a) { e[idx] = a, ne[idx] = head, head = idx ++ ; } void remove () { head = ne[head]; }
双链表 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 int e[N], l[N], r[N], idx;void init () { r[0 ] = 1 , l[1 ] = 0 ; idx = 2 ; } void insert (int a, int x) { e[idx] = x; l[idx] = a, r[idx] = r[a]; l[r[a]] = idx, r[a] = idx ++ ; } void remove (int a) { l[r[a]] = l[a]; r[l[a]] = r[a]; }
栈 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 int stk[N], tt = 0 ;stk[ ++ tt] = x; tt -- ; stk[tt]; if (tt > 0 ){ }``` --- #### 队列 ##### 1. 普通队列: ```cpp int q[N], hh = 0 , tt = -1 ;q[ ++ tt] = x; hh ++ ; q[hh]; if (hh <= tt){ }
2. 循环队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int q[N], hh = 0 , tt = 0 ;q[tt ++ ] = x; if (tt == N) tt = 0 ;hh ++ ; if (hh == N) hh = 0 ;q[hh]; if (hh != tt){ }
单调栈 1 2 3 4 5 6 7 常见模型:找出每个数左边离它最近的比它大/小的数 int tt = 0 ;for (int i = 1 ; i <= n; i ++ ){ while (tt && check (stk[tt], i)) tt -- ; stk[ ++ tt] = i; }
单调队列 1 2 3 4 5 6 7 8 常见模型:找出滑动窗口中的最大值/最小值 int hh = 0 , tt = -1 ;for (int i = 0 ; i < n; i ++ ){ while (hh <= tt && check_out (q[hh])) hh ++ ; while (hh <= tt && check (q[tt], i)) tt -- ; q[ ++ tt] = i; }
KMP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 求模式串的Next数组: for (int i = 2 , j = 0 ; i <= m; i ++ ){ while (j && p[i] != p[j + 1 ]) j = ne[j]; if (p[i] == p[j + 1 ]) j ++ ; ne[i] = j; } for (int i = 1 , j = 0 ; i <= n; i ++ ){ while (j && s[i] != p[j + 1 ]) j = ne[j]; if (s[i] == p[j + 1 ]) j ++ ; if (j == m) { j = ne[j]; } }
Trie树 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 int son[N][26 ], cnt[N], idx;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]; i ++ ) { int u = str[i] - 'a' ; if (!son[p][u]) return 0 ; p = son[p][u]; } return cnt[p]; }
并查集 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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 (1 )朴素并查集: int p[N]; int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } for (int i = 1 ; i <= n; i ++ ) p[i] = i; p[find (a)] = find (b); (2 )维护size的并查集: int p[N], size[N]; int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } for (int i = 1 ; i <= n; i ++ ) { p[i] = i; size[i] = 1 ; } size[find (b)] += size[find (a)]; p[find (a)] = find (b); (3 )维护到祖宗节点距离的并查集: int p[N], d[N]; int find (int x) { if (p[x] != x) { int u = find (p[x]); d[x] += d[p[x]]; p[x] = u; } return p[x]; } for (int i = 1 ; i <= n; i ++ ) { p[i] = i; d[i] = 0 ; } p[find (a)] = find (b); d[find (a)] = distance;
堆 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 int h[N], ph[N], hp[N], size;void heap_swap (int a, int b) { swap (ph[hp[a]],ph[hp[b]]); swap (hp[a], hp[b]); swap (h[a], h[b]); } void down (int u) { int t = u; if (u * 2 <= size && h[u * 2 ] < h[t]) t = u * 2 ; if (u * 2 + 1 <= size && h[u * 2 + 1 ] < h[t]) t = u * 2 + 1 ; if (u != t) { heap_swap (u, t); down (t); } } void up (int u) { while (u / 2 && h[u] < h[u / 2 ]) { heap_swap (u, u / 2 ); u >>= 1 ; } } for (int i = n / 2 ; i; i -- ) down (i);
一般哈希 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 (1 ) 拉链法 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 ; } (2 ) 开放寻址法 int h[N]; int find (int x) { int t = (x % N + N) % N; while (h[t] != null && h[t] != x) { t ++ ; if (t == N) t = 0 ; } return t; }
字符串哈希 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 核心思想:将字符串看成P进制数,P的经验值是131 或13331 ,取这两个值的冲突概率低 小技巧:取模的数用2 ^64 ,这样直接用unsigned long long 存储,溢出的结果就是取模的结果 typedef unsigned long long ULL;ULL h[N], p[N]; p[0 ] = 1 ; for (int i = 1 ; i <= n; i ++ ){ h[i] = h[i - 1 ] * P + str[i]; p[i] = p[i - 1 ] * P; } ULL get (int l, int r) { return h[r] - h[l - 1 ] * p[r - l + 1 ]; }
C++ STL简介 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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 vector, 变长数组,倍增的思想 size () 返回元素个数 empty () 返回是否为空 clear () 清空 front ()/back () push_back ()/pop_back () begin ()/end () [] 支持比较运算,按字典序 pair<int , int > first, 第一个元素 second, 第二个元素 支持比较运算,以first为第一关键字,以second为第二关键字(字典序) string,字符串 size ()/length () 返回字符串长度 empty () clear () substr (起始下标,(子串长度)) 返回子串 c_str () 返回字符串所在字符数组的起始地址 queue, 队列 size () empty () push () 向队尾插入一个元素 front () 返回队头元素 back () 返回队尾元素 pop () 弹出队头元素 priority_queue, 优先队列,默认是大根堆 size () empty () push () 插入一个元素 top () 返回堆顶元素 pop () 弹出堆顶元素 定义成小根堆的方式:priority_queue<int , vector<int >, greater<int >> q; stack, 栈 size () empty () push () 向栈顶插入一个元素 top () 返回栈顶元素 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 () ++, -- 返回前驱和后继,时间复杂度 O (logn) set/multiset insert () 插入一个数 find () 查找一个数 count () 返回某一个数的个数 erase () (1 ) 输入是一个数x,删除所有x O (k + logn) (2 ) 输入一个迭代器,删除这个迭代器 lower_bound () /upper_bound () lower_bound (x) 返回大于等于x的最小的数的迭代器 upper_bound (x) 返回大于x的最小的数的迭代器 map/multimap insert () 插入的数是一个pair erase () 输入的参数是pair或者迭代器 find () [] 注意multimap不支持此操作。 时间复杂度是 O (logn) lower_bound () /upper_bound () unordered_set, unordered_map, unordered_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位取反