https://www.acwing.com/activity/content/punch_the_clock/11/
单链表
数组实现链表
#include <iostream> using namespace std; const int N = 1e5 + 10; // head 表示头结点的下标 // e[i] 表示节点i的值 // ne[i] 表示节点i的next指针是多少 // idx 存储当前已经用到了哪个点 int head, e[N], ne[N], idx; // 初始化 void init(){ head = -1; idx = 1; } // 将x插到头结点 void add_to_head(int x){ e[idx] = x; ne[idx] = head; head = idx; idx++; } // 将x插到下标是k的点后面 void insert(int k, int x){ e[idx] = x; ne[idx] = ne[k]; ne[k] = idx; idx++; } // 将下标是k的点后面的点删掉 void remove(int k){ ne[k] = ne[ne[k]]; } int main(){ int m; cin >> m; init(); while(m--){ char oper; int k, x; cin >> oper; if(oper == 'H'){ cin >> x; add_to_head(x); }else if(oper == 'D'){ cin >> k; if(k == 0) head = ne[head]; else remove(k); }else{ cin >> k >> x; insert(k, x); } } for(int i = head; i != -1; i = ne[i]){ cout << e[i] << " "; } return 0; }
双链表
#include <iostream> using namespace std; const int N = 1e5 + 10; int m, k, x; string oper; int e[N], l[N], r[N], idx; //在右边插入 void insert(int k, int x){ e[idx] = x; l[idx] = k; r[idx] = r[k]; l[r[k]] = idx; r[k] = idx; idx++; } void remove(int k){ r[l[k]] = r[k]; l[r[k]] = l[k]; } int main(){ cin >> m; // 0是左端点,1是右端点 r[0] = 1, l[1] = 0, idx = 2; while(m--){ cin >> oper; if(oper == "L"){ cin >> x; insert(0, x); }else if(oper == "R"){ cin >> x; insert(l[1], x); }else if(oper == "D"){ cin >> k; remove(k + 1); }else if(oper == "IL"){ cin >> k >> x; insert(l[k + 1], x); }else{ cin >> k >> x; insert(k + 1, x); } } for(int i = r[0]; i != 1; i = r[i]){ cout << e[i] << " "; } cout << endl; return 0; }
模拟栈
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int e[N], ne[N], idx; //在尾部插入 void push(int x){ e[idx] = x; idx++; } void pop(){ idx--; } int get_top(){ return e[idx - 1]; } bool is_Empty(){ return idx <= 0; } int main(){ int m, x; cin >> m; string oper; idx = 0; while(m--){ cin >> oper; if(oper == "push"){ cin >> x; push(x); }else if(oper == "pop"){ pop(); }else if(oper == "empty"){ if(is_Empty()) printf("YES\n"); else printf("NO\n"); }else{ cout << get_top() << endl; } } }
表达式求值
#include <bits/stdc++.h> using namespace std; stack<int> num; stack<char> op; void eval(){ int b = num.top(); num.pop(); int a = num.top(); num.pop(); char c = op.top(); op.pop(); int x; if (c == '+') x = a + b; else if (c == '-') x = a - b; else if (c == '*') x = a * b; else x = a / b; num.push(x); } int main(){ unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; string s; cin >> s; int n = s.size(); for(int i = 0; i < n; i++){ auto c = s[i]; if(isdigit(c)){ int x = 0, j = i; while(j < n && isdigit(s[j])){ x = x * 10 + s[j++] - '0'; } num.push(x); i = j - 1; }else if(c == '('){ op.push(c); }else if(c == ')'){ while(op.top() != '(') eval(); op.pop(); }else{ while(op.size() && op.top() != '(' && pr[c] <= pr[op.top()]) eval(); op.push(c); } } while(op.size()) eval(); printf("%d\n", num.top()); }
模拟队列
int head, e[N], idx; //在尾部插入 void push(int x){ e[idx] = x; idx++; } void pop(){ head++; } int get_top(){ return e[head]; } bool is_Empty(){ return head >= idx; }
单调栈
int stk[N], tt; int main(){ int n; cin >> n; tt = 0; for(int i = 0; i < n; i++){ int x; cin >> x; while(tt && stk[tt] >= x) tt--; if(tt) printf("%d ", stk[tt]); else printf("-1 "); stk[++tt] = x; } return 0; }
单调队列
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int a[N], q[N]; int main(){ int n, k; cin >> n >> k; int minn = INT_MIN, maxn = INT_MAX; for(int i = 0; i < n; i++){ cin >> a[i]; } int hh = 0, tt = -1; for (int i = 0; i < n; i ++ ){ if(hh <= tt && i - k + 1 > q[hh]) hh++ ; while (hh <= tt && a[q[tt]] >= a[i]) tt-- ; q[++tt] = i; if (i >= k - 1) printf("%d ", a[q[hh]]); } printf("\n"); hh = 0, tt = -1; for (int i = 0; i < n; i ++ ){ if(hh <= tt && i - k + 1 > q[hh]) hh++ ; while (hh <= tt && a[q[tt]] <= a[i]) tt-- ; q[++tt] = i; if (i >= k - 1) printf("%d ", a[q[hh]]); } return 0; }
KMP
时间复杂度O(n+m)
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int N = 100010, M = 1000010; int n, m; char s[M], p[N]; int nextt[N]; int main(){ cin >> n >> p + 1 >> m >> s + 1; //字符串从下标1开始 //计算next数组 for(int i = 2, j = 0; i <= n; i++){ while(j && p[i] != p[j + 1]) j = nextt[j]; if(p[i] == p[j + 1]) j++; nextt[i] = j; } //KMP匹配过程 for(int i = 1, j = 0; i <= m; i++){ while(j && s[i] != p[j + 1]) j = nextt[j]; if(s[i] == p[j + 1]) j++; if(j == n){ printf("%d ", i - n); //这里要求输出的下标从0开始计数 j = nextt[j]; } } return 0; }
Trie
高效地存储和查找字符串集合的数据结构
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n; string x; char op; int son[N][26], cnt[N], idx; //idx是节点序号,son[i][u]代表值为u的节点i的子节点 cnt[i]代表以节点i结尾的字符串个数 void insert(string x){ int p = 0; for(int i = 0; i < x.size(); i++){ int u = x[i] - 'a'; if(!son[p][u]) son[p][u] = ++idx; p = son[p][u]; } cnt[p]++; } int query(string x){ int p = 0; for(int i = 0; i < x.size(); i++){ int u = x[i] - 'a'; if(!son[p][u]) return 0; p = son[p][u]; } return cnt[p]; } int main(){ cin >> n; while(n--){ cin >> op >> x; if(op == 'I'){ insert(x); }else{ printf("%d\n", query(x)); } } return 0; }
最大异或树
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10, M = 3100010; int n; int a[N], son[M][2], idx; //idx是节点序号,son[i][u]代表值为u的节点i的子节点 void insert(int x){ int p = 0; for(int i = 30; i >= 0; i--){ int &s = son[p][x >> i & 1]; //用 int & 是C++里的引用,和指针类似,定义的变量和赋值的变量指向同一块内存,一个改变,另一个也会改变。 if(!s) s = ++idx; p = s; } } int search(int x){ int p = 0, res = 0; for(int i = 30; i >= 0; i--){ int u = x >> i & 1; if(son[p][!u]) { res += 1 << i; p = son[p][!u]; } else p = son[p][u]; } return res; } int main(){ cin >> n; for (int i = 0; i < n; i++){ scanf("%d", &a[i]); insert(a[i]); } int res = 0; for (int i = 0; i < n; i++) res = max(res, search(a[i])); printf("%d\n", res); return 0; }
并查集
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int p[N]; int find(int x){ if(p[x] != x) p[x] = find(p[x]); return p[x]; } int main(){ int n, m, a, b; cin >> n >> m; for(int i = 1; i <= n; i++) p[i] = i; while(m--){ char op; cin >> op >> a >> b; if(op == 'M'){ p[find(a)] = find(b); }else{ if(find(a) == find(b)) printf("Yes\n"); else printf("No\n"); } } return 0; }
堆排序和模拟堆
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n, m; int h[N], cnt; void down(int u){ int t = u; if(u * 2 <= cnt && h[2 * u] < h[t]) t = 2 * u; if(u * 2 + 1 <= cnt && h[2 * u + 1] < h[t]) t = 2 * u + 1; if(t != u){ swap(h[t], h[u]); down(t); } } void up(int u){ while(u / 2 && h[u] < h[u / 2]){ heap_swap(u, u / 2); u /= 2; } } int main(){ cin >> n >> m; for(int i = 1; i <= n; i++) cin >> h[i]; cnt = n; for(int i = n / 2; i; i--) down(i); while(m--){ printf("%d ", h[1]); h[1] = h[cnt]; cnt--; down(1); } return 0; }
哈希表
- 哈希表的存储结构
- 开放寻址法
提问:“开放寻址法的那个memset填充h不应该是0x3f3f3f3f吗?为什么变成了0x3f?,还有奥,按照这个办法来说的话,当h数组满了的话,应该会陷入死循环把”
回答:①memset是按字节来初始化的,int中有四个字节,初始化成0x3f就是将每个字节都初始化成0x3f,所以每个int就是 0x3f3f3f3f。
②数组长度一般开到个数上限的2-3倍。 - 拉链法
- 开放寻址法
- 字符串哈希方式
开放寻址法,y总还讲了拉链法,代码也有,可以去看看。
#include <bits/stdc++.h> using namespace std; const int N = 200003, null = 0x3f3f3f3f; int n, x; 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; } int main(){ memset(h, 0x3f, sizeof(h)); cin >> n; while(n--){ string op; cin >> op >> x; if(op == "I"){ h[find(x)] = x; }else{ if (h[find(x)] == null) puts("No"); else puts("Yes"); } } return 0; }
字符串哈希
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ULL; const int N = 100010, P = 131; int n, m; char str[N]; ULL h[N], p[N]; int 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; }