这篇记录一些算法竞赛和刷题中常用的 C 语言手写模板,包括哈希表、堆、快速排序和二分查找。方便之后随时查阅和复制。
1. 手写哈希表
使用「开放寻址法」实现的整数哈希表,适合整数键值对的快速存取。
#include <stdio.h>
#include <string.h>
#define N 200003 // 通常取大于数据量 2~3 倍的一个质数
#define NUL 0x3f3f3f3f // 空标记(需保证不会与实际键冲突)
int h[N];
int find(int x) {
int k = (x % N + N) % N; // 处理负数
while (h[k] != NUL && h[k] != x) {
k++;
if (k == N) k = 0;
}
return k;
}
void insert(int x) {
int k = find(x);
h[k] = x;
}
int contains(int x) {
int k = find(x);
return h[k] == x; // 存在返回1,否则返回0
}
int main() {
memset(h, 0x3f, sizeof(h));
insert(10);
insert(-3);
printf("%d\n", contains(10)); // 1
printf("%d\n", contains(5)); // 0
return 0;
}
2. 手写堆(小根堆)
数组实现的小根堆,支持插入和弹出最小值。
#include <stdio.h>
#define MAXN 100010
int heap[MAXN], sz;
void swap(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}
void up(int u) {
while (u / 2 && heap[u / 2] > heap[u]) {
swap(&heap[u / 2], &heap[u]);
u /= 2;
}
}
void down(int u) {
int t = u;
if (u * 2 <= sz && heap[u * 2] < heap[t]) t = u * 2;
if (u * 2 + 1 <= sz && heap[u * 2 + 1] < heap[t]) t = u * 2 + 1;
if (t != u) {
swap(&heap[t], &heap[u]);
down(t);
}
}
void push(int x) {
heap[++sz] = x;
up(sz);
}
int pop() { // 弹出并返回堆顶(最小值)
int top = heap[1];
heap[1] = heap[sz--];
down(1);
return top;
}
int main() {
push(5); push(3); push(8);
printf("%d\n", pop()); // 3
printf("%d\n", pop()); // 5
return 0;
}
3. 快速排序
标准快排模板,以中间元素为基准,hoare 风格分区。
#include <stdio.h>
void swap(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}
void quick_sort(int q[], int l, int r) {
if (l >= r) return;
int x = q[(l + r) >> 1]; // 基准取中间值
int i = l - 1, j = r + 1;
while (i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(&q[i], &q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
int main() {
int q[] = {3, 1, 4, 1, 5, 9, 2, 6};
int n = sizeof(q) / sizeof(q[0]);
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
4. 二分查找
两种常用模板:查找左边界(第一个大于等于 target 的位置)和右边界(最后一个小于等于 target 的位置)。
#include <stdio.h>
// 查找左边界:第一个 >= x 的位置
int lower_bound(int a[], int n, int x) {
int l = 0, r = n - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
// 查找右边界:最后一个 <= x 的位置
int upper_bound(int a[], int n, int x) {
int l = 0, r = n - 1;
while (l < r) {
int mid = (l + r + 1) >> 1; // 注意 +1 避免死循环
if (a[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
int a[] = {1, 2, 2, 3, 4, 5, 5, 6};
int n = 8;
printf("%d\n", lower_bound(a, n, 2)); // 1
printf("%d\n", upper_bound(a, n, 2)); // 2
return 0;
}
以上就是目前整理的几个常用模板。