--- title: 数据结构与算法实验 ACM模式备忘录 (C++) date: 2026-01-15 descriptionHTML: '整理了ACM赛制/数据结构考试中常用的C++模板与算法讲解' tags: - 笔记 - C++ - 算法 sidebar: true readingTime: true hidden: false recommend: true --- # 数据结构与算法实验 备忘录 (C++) 这份笔记整理了ACM赛制/数据结构考试中常用的C++模板与算法讲解。 ## 1. 基础环境 (Template) ```cpp #include #include #include #include #include #include #include // for greater using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; int main() { // 核心:关闭同步,加速cin/cout ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); // 在这里写你的代码 return 0; } ``` --- ## 2. 基础数据结构与 STL ### 2.1 指针与引用 (Pointers & References) **这是很多同学容易混淆的地方,尤其是在函数传参时。** #### 1. 如何定义 ```cpp int a = 10; int* p = &a; // p 是指针,存储 a 的地址 int& r = a; // r 是引用,r 就是 a 的别名 (即 r 和 a 是同一个东西) ``` #### 2. 函数传参:值传递 vs 引用传递 vs 指针传递 **场景**:你需要在一个函数里修改外面的变量,或者传递大数组不希望发生拷贝。 **A. 引用传递 (`Type& name`) —— 最推荐** 如果函数参数里写了 `&`,你在函数里**直接把它当成普通变量用**就行。 ```cpp // 这里的 x 就是外面传进来的那个变量的“分身” void modify(int& x) { x = 100; // 不需要加 *,直接修改 } int main() { int num = 0; modify(num); // num 变成 100 } ``` **B. 指针传递 (`Type* name`)** 需要传地址,函数里要用 `*` 解引用。 ```cpp void modifyPtr(int* p) { if (p == NULL) return; *p = 100; // *p 代表指针指向的那个变量 } int main() { int num = 0; modifyPtr(&num); // 注意:要取地址符 & } ``` **总结**: * 看到 `void func(int &x)` -> 内部直接用 `x`,改变会影响外面。 * 看到 `void func(int *x)` -> 内部用 `*x` 访问值,调用时传地址 `&a`。 * 链表/树结构通常用指针 (`ListNode*`),因为可能为空 (`NULL`)。 ### 2.2 常用 STL 容器 **Stack (栈)**: 先进后出 (LIFO),用于括号匹配、DFS。 ```cpp stack s; s.push(1); s.pop(); s.top(); s.empty(); ``` **Queue (队列)**: 先进先出 (FIFO),用于BFS、层序遍历。 ```cpp queue q; q.push(1); q.pop(); q.front(); q.empty(); ``` **Vector (动态数组)**: ```cpp vector v; v.push_back(1); sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); // 排序后去重 ``` **Priority Queue (优先队列)**: 默认大根堆。 ```cpp priority_queue pq; // 大根堆 priority_queue, greater> min_pq; // 小根堆 ``` ### 2.2 单链表 (Linked List) **技巧**: 使用虚拟头结点 (dummy head) 处理删除操作。 ```cpp struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; // 1. 尾插法创建 ListNode* createList(const vector& nums) { ListNode* dummy = new ListNode(0); ListNode* tail = dummy; for (int x : nums) { tail->next = new ListNode(x); tail = tail->next; } return dummy->next; } // 2. 反转链表 ListNode* reverseList(ListNode* head) { ListNode *prev = NULL, *curr = head; while (curr) { ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } return prev; } // 3. 删除指定值的节点 ListNode* removeElements(ListNode* head, int val) { ListNode* dummy = new ListNode(0); dummy->next = head; ListNode* cur = dummy; while (cur->next) { if (cur->next->val == val) { ListNode* temp = cur->next; cur->next = cur->next->next; delete temp; } else { cur = cur->next; } } return dummy->next; } ``` --- ## 3. 基础算法 ### 3.1 二分查找 (Binary Search) **适用**: 有序数组中查找。 ```cpp // 查找第一个 >= target 的位置 int lower_bound_custom(const vector& arr, int target) { int l = 0, r = arr.size() - 1, ans = -1; while (l <= r) { int mid = l + (r - l) / 2; if (arr[mid] >= target) { ans = mid; r = mid - 1; } else { l = mid + 1; } } return ans; } ``` ### 3.2 快速幂 (Quick Power) **功能**: 计算 $a^b \pmod m$。 ```cpp ll qpow(ll a, ll b, ll m) { ll res = 1; a %= m; while (b) { if (b & 1) res = (res * a) % m; a = (a * a) % m; b >>= 1; } return res; } ``` --- ## 4. 字符串算法 ### KMP 算法 **功能**: 字符串匹配,求 pattern 在 text 中的位置。 ```cpp // 求 next 数组 vector getNext(string p) { int m = p.size(), j = 0; vector nxt(m); nxt[0] = 0; for (int i = 1; i < m; i++) { while (j > 0 && p[i] != p[j]) j = nxt[j - 1]; if (p[i] == p[j]) j++; nxt[i] = j; } return nxt; } // KMP 匹配 int kmp(string text, string pattern) { if (pattern.empty()) return 0; vector nxt = getNext(pattern); int j = 0; for (int i = 0; i < text.size(); i++) { while (j > 0 && text[i] != pattern[j]) j = nxt[j - 1]; if (text[i] == pattern[j]) j++; if (j == pattern.size()) return i - pattern.size() + 1; } return -1; } ``` --- ## 5. 树 (Tree) ### 5.1 二叉树遍历 ```cpp struct TreeNode { int val; TreeNode *left, *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; // 递归遍历 (Pre/In/Post) void dfs(TreeNode* root) { if (!root) return; // cout << root->val; // 先序 dfs(root->left); // cout << root->val; // 中序 dfs(root->right); // cout << root->val; // 后序 } // 层序遍历 (BFS) void levelOrder(TreeNode* root) { if (!root) return; queue q; q.push(root); while (!q.empty()) { TreeNode* node = q.front(); q.pop(); cout << node->val << " "; if (node->left) q.push(node->left); if (node->right) q.push(node->right); } } ``` ### 5.2 并查集 (Union-Find) **功能**: 集合合并与查询。 ```cpp int fa[10005]; void init(int n) { for(int i=1; i<=n; i++) fa[i]=i; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } // 路径压缩 void unite(int x, int y) { fa[find(x)] = find(y); } ``` ### 5.3 树状数组 (Fenwick Tree) **功能**: 单点修改,区间查询前缀和。 ```cpp int bit[10005], n; int lowbit(int x) { return x & -x; } void add(int x, int k) { for(; x<=n; x+=lowbit(x)) bit[x]+=k; } int query(int x) { int s=0; for(; x>0; x-=lowbit(x)) s+=bit[x]; return s; } ``` --- ## 6. 图论 (Graph) ### 6.1 存图与遍历 (Adjacency List, DFS, BFS) ```cpp struct edge { int to, w; }; vector> adj; // adj[u] 存 u 的出边 vector vis; // DFS void dfs(int u) { vis[u] = true; for (auto e : adj[u]) { if (!vis[e.to]) dfs(e.to); } } // BFS (求无权图最短路) void bfs(int s) { queue q; q.push(s); vis[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); for (auto e : adj[u]) { if (!vis[e.to]) { vis[e.to] = true; q.push(e.to); } } } } ``` ### 6.2 最短路 Dijkstra (堆优化) **注意**: 仅适用于非负权边。 ```cpp vector dis; void dijkstra(int s, int n) { dis.assign(n + 1, INF); dis[s] = 0; priority_queue, vector>, greater>> pq; pq.push({0, s}); while (!pq.empty()) { int d = pq.top().first; int u = pq.top().second; pq.pop(); if (d > dis[u]) continue; for (auto e : adj[u]) { if (dis[u] + e.w < dis[e.to]) { dis[e.to] = dis[u] + e.w; pq.push({dis[e.to], e.to}); } } } } ``` ### 6.3 最小生成树 (Kruskal) **原理**: 贪心,按边权从小到大选边,并查集判环。 ```cpp struct Edge { int u, v, w; bool operator<(const Edge& o) const { return w < o.w; } } edges[20005]; int kruskal(int n, int m) { sort(edges, edges + m); init(n); // 并查集初始化 int res = 0, cnt = 0; for (int i = 0; i < m; i++) { if (find(edges[i].u) != find(edges[i].v)) { unite(edges[i].u, edges[i].v); res += edges[i].w; cnt++; } } return cnt == n - 1 ? res : -1; } ``` ### 6.4 拓扑排序 (Topological Sort) **功能**: 有向无环图 (DAG) 的排序。 ```cpp int in_degree[10005]; // 需预处理入度 vector toposort(int n) { queue q; for(int i=1; i<=n; i++) if(in_degree[i]==0) q.push(i); vector res; while(!q.empty()){ int u = q.front(); q.pop(); res.push_back(u); for(auto e : adj[u]){ if(--in_degree[e.to] == 0) q.push(e.to); } } return res; } ```