常用代码模板
单链表
// 前项星
// head存储表头, e[]存储节点的值, ne[]存储节点的next指针,idx表示当前用到了那个节点
int head, e[N], ne[N], idx;
// 初始化
void init() {
head = -1;
idx = 0;
}
// 在链表头插入一个数a
void insert(int a) {
e[idx] = a;
ne[idx] = head;
head = idx ++;
}
// 将头结点删除, 需要保证头结点存在
void remove() {
head = ne[head];
}
双链表
// e[] 表示节点的值, l[]表示节点的左指针, r[]表示节点的右指针, idx表示当前用到了那个节点
//初始化
void init() {
// 0是端点, 1是右端点
r[0] = 1;
l[1] = 0;
idx = 3;
}
// 在节点a的右边插入一个数x
void insert(int a, int x) {
e[idx] = x;
l[idx] = a;
r[idx] = r[a];
l[r[a]] = idx;
r[a] = idx ++;
}
// 删除节点a
void remove(int a) {
l[r[a]] = l[a];
r[l[a]] = r[a];
}
栈
// stk[0] 不使用
// top 表示栈顶
int stk[N], top = 0;
//向栈顶插入
stk[ ++ top] = x;
// 从栈顶弹出一个数
top --;
// 栈顶的值
stk[top];
// 判断栈是否为空
if (top > 0) {
}
队列
// 普通队列
// head 指向队头元素, tail指向队尾元素
int q[N], head = 0, tail = -1;
// 队尾插入
q[++ tail] = x;
// 队头弹出
head ++;
// 队头的值ds
q[head];
// 判断队列是否为空
if (head <= tail) {
}
// 循环队列 (少用, 队空和队满的判别条件容易混淆)
// head表示队头, tail 表示队尾的下一个位置
int q[N], head = 0, tail = 0;
// 向队尾插入一个数
q[tail] = x;
tail = (tail + 1) % N;
// 队头弹出一个数
head = (head + 1) % N;
// 队头的值
q[head];
//判断队列是否为空
if (head != tail) {
}
单调栈
int top = 0;
for (int i = 1; i <= n; ++ i) {
while (top && check(stk[top], i)) top --;
stk[++ top] = i;
}
单调队列
int he = 0, ta = -1;
for (int i = 0; i < n; i ++) {
while (he <= ta && check_out(q[he])) he ++;
while (he <= ta && check(q[ta], i)) ta --;
q[++ ta] = i;
}
KMP
// s[1,2,...,n]是长文本, p[1,2,...,m]是模式串
// 求模式串的Next数组
nxt[1] = 0;
for (int i = 2, j = 0; i <= m; ++ i) {
while (j && p[i] != p[j + 1])
j = nxt[j];
if (p[i] == p[j + 1])
++ j;
nxt[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; ++ i) {
while (j && s[i] != p[j + 1])
j = nxt[j];
if (s[i] == p[j + 1])
++ j;
if (j == m) {
j = nxt[j];
// 匹配成功后的逻辑
}
}
// 其他写法
// pi[0,1,...,n-1] 表示s[0,...,n]最长的相等的真前缀与真后缀的长度
vector<int> prefix_function(string s) {
int n = (int)s.length();
vector<int> pi(n);
for (int i = 1; i < n; ++ i) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j])
j = pi[j - 1];
if (s[i] == s[j])
++j
pi[i] =j;
}
return pi;
}
Trie树
int son[N][26], cnt[N], idx;
// 0号节点是根节点,空节点
// son[][] 储存树中每个节点的子节点
// cnt[] 储存以每个节点结尾的单词数量
//插入一个字符串
void insert(char *str, int l) {
int p = 0;
for (int i = 0; i < l; ++ 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 l) {
int p = 0;
for (int i = 0; i < l; ++ i) {
int u = str[i] -'a';
if (!son[p][u])
return 0;
p = son[p][u];
}
return cnt[p];
}
并查集
const int N = 10000; //
namespace union_set {
int father[N], size[N];
int _find(int x) {
int temp = x;
while (x != father[x])
x = father[x];
while (temp != father[temp]) { // 路径压缩
int z = father[temp];
father[temp] = x;
temp = z;
}
return x;
}
void _union(int a, int b) {
int fa = _find(a);
int fb = _find(b);
father[fb] = fa;
size[fa] += size[fb];
}
}
堆
// 大根堆
const int MAXN = 1000;
int heap[MAXN], n = 10;
void downAdjust(int low, int high) {
int i = low, j = i * 2;
while (j <= high) {
if (j + 1 <= high && heap[j + 1] > heap[j])
j = j + 1;
if (heap[i] < heap[j])
swap(heap[i], heap[j]);
else
break;
i = j;
j = i * 2;
}
}
void upAdjust(int low, int high) {
int i = high, j = i / 2;
while (j >= low) {
if (heap[j] < heap[i])
swap(heap[i], heap[j]);
else
break;
i = j;
j = i / 2;
}
}
void insert(int x) {
heap[++n] = x;
upAdjust(1, n);
}
void deleteTop() {
heap[1] = heap[n--];
downAdjust(1, n);
}
void heapSort() {
createHeap();
for (int i = n / 2; i >= 1; -- i) {
swap(heap[i], heap[1]);
downAdjust(1, i - 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;
}
// 如果实现删除, 只要用一个标记数组标记是否删除,不需要真正从内存中删除
- 开放寻址法
int h[N];
// 如果x在哈希表中,返回x的下标,如果x不在哈希表中,返回x应该插入的位置
int find(int x) {
int t = (x % N + N) % N;
while (h[t] != INF && h[t] != x) {
t++;
if (t == N)
t = 0;
}
return t;
}
字符串哈希
// 核心思想:将字符串看成P进制数,P的经验值是131或者13331,
//小技巧:取模的数用2^64, 这样用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
// h[k]存储字符串前k个字母的哈希值, p[k]存储P^k mod 2^64
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;
}
// 计算子串str[l~r] 的哈希值
ULL get(int l, int r) {
return h[r] - h[l - 1] * p[r - l + 1];
}
树与图的存储
- 邻接矩阵
G[a][b] 表示a->b
- 邻接表
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;
// 加一条边
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 初始化
idx = 0;
memset(h, -1, sizeof(h));
树与图的存储
时间复杂度\(O(n+m)\)
- 深度优先遍历
int dfs(int u) {
st[u] = true;
for (int i = h[u];i != -1; ++i) {
int j = e[i];
if (!st[j])
dfs(j);
}
}
- 宽度优先遍历

浙公网安备 33010602011771号