|
| 1 | +# 目录 |
| 2 | + |
| 3 | +## 第一章 数据结构与算法总览 |
| 4 | + |
| 5 | +[1. 为什么 AI 算法岗仍然必须掌握数据结构与算法?](#q-001) |
| 6 | + - [面试问题:数据结构和算法在 AIGC 系统中分别解决什么问题?](#q-002) |
| 7 | + - [面试问题:如何从 AI 工程场景选择合适的数据结构?](#q-003) |
| 8 | + - [面试问题:算法复杂度为什么会影响模型训练和推理成本?](#q-004) |
| 9 | + |
| 10 | +## 第二章 复杂度、内存与工程权衡 |
| 11 | + |
| 12 | +[2. 时间复杂度和空间复杂度应该如何分析?](#q-005) |
| 13 | + - [面试问题:最好、最坏、平均和摊还复杂度有什么区别?](#q-006) |
| 14 | + - [面试问题:为什么常数、缓存局部性和内存访问在 AI 系统中很重要?](#q-007) |
| 15 | + - [面试问题:如何理解空间换时间、预计算和缓存?](#q-008) |
| 16 | + |
| 17 | +## 第三章 常用结构技术地图 |
| 18 | + |
| 19 | +[3. 常用数据结构如何分类?](#q-009) |
| 20 | + - [面试问题:数组、链表、栈、队列、哈希表、树、图分别适合什么场景?](#q-010) |
| 21 | + - [面试问题:哪些数据结构在大模型系统中最常见?](#q-011) |
| 22 | + - [面试问题:刷题中的数据结构如何迁移到真实 AI 项目?](#q-012) |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +<h1 id="q-001">1. 为什么 AI 算法岗仍然必须掌握数据结构与算法?</h1> |
| 27 | + |
| 28 | +AI 行业看起来以模型、论文、GPU 和数据为核心,但真正把模型训练、推理、检索、部署和 Agent 系统做稳,离不开数据结构与算法。 |
| 29 | + |
| 30 | +原因有四点: |
| 31 | + |
| 32 | +1. **模型本身依赖高效数据组织** |
| 33 | + |
| 34 | + Tensor 是多维数组,计算图是有向图,注意力缓存是按 token 和层组织的结构,RAG 索引依赖向量检索结构。 |
| 35 | + |
| 36 | +2. **训练和推理受复杂度约束** |
| 37 | + |
| 38 | + Transformer 注意力从 $O(n^2)$ 到稀疏注意力、滑动窗口注意力、KV Cache,本质都是算法复杂度优化。 |
| 39 | + |
| 40 | +3. **工程系统需要稳定的数据结构** |
| 41 | + |
| 42 | + DataLoader 队列、GPU task scheduler、LRU cache、token buffer、请求优先队列、日志索引、向量数据库都需要基础结构。 |
| 43 | + |
| 44 | +4. **面试仍会考察手写代码能力** |
| 45 | + |
| 46 | + AIGC 算法岗不仅问模型原理,也经常考链表、二分、堆、图、动态规划、字符串匹配、Top K、LRU、BFS/DFS 等。 |
| 47 | + |
| 48 | +本板块中,概念、复杂度和 AI 场景主要在 01-06 展开;需要手写的高频模板统一补充在 [07_三语言代码模板与AI场景实现高频考点.md](07_三语言代码模板与AI场景实现高频考点.md),便于对照 Python、C/C++、Java 三种语言复习。 |
| 49 | + |
| 50 | +**面试金句:** |
| 51 | + |
| 52 | +数据结构决定数据如何被组织,算法决定数据如何被处理。AI 模型负责“学什么”,数据结构与算法决定系统能否在可接受的成本内“跑起来”。 |
| 53 | + |
| 54 | +<h2 id="q-002">面试问题:数据结构和算法在 AIGC 系统中分别解决什么问题?</h2> |
| 55 | + |
| 56 | +| 维度 | 解决的问题 | AIGC 例子 | |
| 57 | +| --- | --- | --- | |
| 58 | +| 数据结构 | 数据如何存储、索引、访问和更新 | Tensor、KV Cache、向量索引、Trie、队列、图 | |
| 59 | +| 算法 | 如何高效完成计算、搜索、排序、匹配和优化 | Top K、Beam Search、二分、图搜索、动态规划 | |
| 60 | +| 复杂度分析 | 判断方案是否能规模化 | 长上下文推理、RAG 检索、批量调度 | |
| 61 | +| 工程权衡 | 在准确率、延迟、吞吐、内存之间取舍 | ANN 检索、缓存策略、调度队列 | |
| 62 | + |
| 63 | +例如 RAG 系统中: |
| 64 | + |
| 65 | +- 文档切片通常存在数组或列表中。 |
| 66 | +- 元数据用哈希表维护。 |
| 67 | +- 向量索引用 HNSW、IVF、PQ 等结构。 |
| 68 | +- 查询时用近似最近邻算法。 |
| 69 | +- 重排时可能用堆维护 Top K。 |
| 70 | +- 会话历史用队列或滑动窗口管理。 |
| 71 | + |
| 72 | +所以 RAG 不是“把文档塞给大模型”,而是一套数据结构和算法系统。 |
| 73 | + |
| 74 | +<h2 id="q-003">面试问题:如何从 AI 工程场景选择合适的数据结构?</h2> |
| 75 | + |
| 76 | +选择数据结构可以按访问模式判断: |
| 77 | + |
| 78 | +| 访问模式 | 优先结构 | AI 场景 | |
| 79 | +| --- | --- | --- | |
| 80 | +| 按下标高频随机访问 | 数组 / Tensor | batch、embedding、feature matrix | |
| 81 | +| 频繁头尾插入删除 | 双端队列 | 流式 token buffer、日志窗口 | |
| 82 | +| 最近最少使用淘汰 | 哈希表 + 双向链表 | KV Cache、RAG query cache、特征缓存 | |
| 83 | +| 快速 key-value 查询 | 哈希表 | tokenizer vocab、样本索引、元数据 | |
| 84 | +| 有序范围查询 | 平衡树 / 跳表 | 时间线、排行榜、检索排序结果 | |
| 85 | +| Top K / 优先调度 | 堆 / 优先队列 | beam search、任务调度、候选召回 | |
| 86 | +| 前缀匹配 | Trie / Radix Tree | tokenizer、prefix cache、约束解码 | |
| 87 | +| 依赖关系 | 图 / DAG | 计算图、workflow、Agent task graph | |
| 88 | +| 大规模相似搜索 | 向量索引 | RAG、推荐、图像检索 | |
| 89 | + |
| 90 | +回答时要补一句工程判断:没有绝对最好的数据结构,只有和访问模式、数据规模、更新频率、内存预算、并发需求最匹配的结构。 |
| 91 | + |
| 92 | +<h2 id="q-004">面试问题:算法复杂度为什么会影响模型训练和推理成本?</h2> |
| 93 | + |
| 94 | +复杂度会直接影响 GPU 显存、吞吐、延迟和云成本。 |
| 95 | + |
| 96 | +典型例子: |
| 97 | + |
| 98 | +1. **注意力复杂度** |
| 99 | + |
| 100 | + 标准 self-attention 对序列长度 $n$ 的复杂度约为 $O(n^2)$。长上下文从 4K 到 128K 时,计算和显存压力不是线性增长。 |
| 101 | + |
| 102 | +2. **KV Cache** |
| 103 | + |
| 104 | + 自回归推理如果每次都重新计算历史 token,成本很高;KV Cache 用空间保存历史 Key/Value,将增量解码成本大幅降低。 |
| 105 | + |
| 106 | +3. **Top K 采样** |
| 107 | + |
| 108 | + vocabulary 很大时,如果每步完整排序是 $O(V\log V)$;使用堆或选择算法可以降低候选筛选成本。 |
| 109 | + |
| 110 | +4. **RAG 检索** |
| 111 | + |
| 112 | + 对千万级向量做精确最近邻搜索成本很高,实际系统常用 HNSW / IVF 等近似索引,用一点召回损失换取低延迟。 |
| 113 | + |
| 114 | +5. **数据加载** |
| 115 | + |
| 116 | + DataLoader 如果没有预取队列、缓存和并行读取,GPU 会等待 CPU 和 IO,导致昂贵算力空转。 |
| 117 | + |
| 118 | +面试中可以说:AI 系统优化不是只改模型结构,也包括选择更合适的数据结构和算法,让每一次计算、检索和调度都更便宜。 |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +<h1 id="q-005">2. 时间复杂度和空间复杂度应该如何分析?</h1> |
| 123 | + |
| 124 | +时间复杂度描述算法运行时间随输入规模增长的趋势,空间复杂度描述额外内存随输入规模增长的趋势。 |
| 125 | + |
| 126 | +常见复杂度从低到高: |
| 127 | + |
| 128 | +| 复杂度 | 含义 | 例子 | |
| 129 | +| --- | --- | --- | |
| 130 | +| $O(1)$ | 常数时间 | 数组下标访问、哈希表平均查询 | |
| 131 | +| $O(\log n)$ | 对数时间 | 二分查找、堆调整、平衡树查询 | |
| 132 | +| $O(n)$ | 线性时间 | 遍历数组、链表扫描 | |
| 133 | +| $O(n\log n)$ | 线性对数 | 归并排序、堆排序、平均快排 | |
| 134 | +| $O(n^2)$ | 平方时间 | 冒泡排序、标准注意力矩阵 | |
| 135 | +| $O(2^n)$ | 指数时间 | 子集枚举、暴力搜索 | |
| 136 | + |
| 137 | +复杂度分析不是为了背公式,而是判断方案能否在真实规模下运行。 |
| 138 | + |
| 139 | +<h2 id="q-006">面试问题:最好、最坏、平均和摊还复杂度有什么区别?</h2> |
| 140 | + |
| 141 | +| 类型 | 含义 | 例子 | |
| 142 | +| --- | --- | --- | |
| 143 | +| 最好复杂度 | 最理想输入下的复杂度 | 插入排序在已有序数组上是 $O(n)$ | |
| 144 | +| 最坏复杂度 | 最糟糕输入下的复杂度 | 快排选坏 pivot 可退化到 $O(n^2)$ | |
| 145 | +| 平均复杂度 | 输入按某种分布时的期望复杂度 | 哈希表平均查询 $O(1)$ | |
| 146 | +| 摊还复杂度 | 多次操作平均下来的复杂度 | 动态数组 append 摊还 $O(1)$ | |
| 147 | + |
| 148 | +AI 工程中的例子: |
| 149 | + |
| 150 | +- 动态 batch 队列扩容偶尔很慢,但摊还成本低。 |
| 151 | +- 哈希表平均很快,但恶意 key 或极端冲突会退化。 |
| 152 | +- ANN 检索平均延迟低,但尾延迟 P99 仍需监控。 |
| 153 | +- Beam Search 平均候选不多,但大 beam size 会显著增加计算。 |
| 154 | + |
| 155 | +面试中要说明:生产系统更关注最坏情况和尾延迟,不能只看平均复杂度。 |
| 156 | + |
| 157 | +<h2 id="q-007">面试问题:为什么常数、缓存局部性和内存访问在 AI 系统中很重要?</h2> |
| 158 | + |
| 159 | +大 O 只描述增长趋势,不描述常数和硬件行为。AI 系统中,常数和内存访问非常关键。 |
| 160 | + |
| 161 | +原因: |
| 162 | + |
| 163 | +- GPU / CPU 访问连续内存更快。 |
| 164 | +- 数组比链表更容易利用 CPU cache。 |
| 165 | +- Tensor 连续布局能提高向量化和矩阵乘性能。 |
| 166 | +- 小批量请求中,函数调用、序列化、锁竞争的常数开销很明显。 |
| 167 | +- Python 层循环可能远慢于 NumPy / PyTorch 的底层向量化实现。 |
| 168 | + |
| 169 | +例子: |
| 170 | + |
| 171 | +- 在深度学习中,同样是 $O(n)$,PyTorch tensor 操作通常比 Python list 循环快很多。 |
| 172 | +- Transformer 推理中,KV Cache 的物理布局会影响吞吐。 |
| 173 | +- 向量数据库中,PQ 压缩可以减少内存带宽压力。 |
| 174 | + |
| 175 | +面试中可以说:复杂度决定上限,内存布局和硬件友好性决定真实性能。 |
| 176 | + |
| 177 | +<h2 id="q-008">面试问题:如何理解空间换时间、预计算和缓存?</h2> |
| 178 | + |
| 179 | +空间换时间是用额外存储减少重复计算。 |
| 180 | + |
| 181 | +典型例子: |
| 182 | + |
| 183 | +| 技术 | 用额外空间保存什么 | 换来的收益 | |
| 184 | +| --- | --- | --- | |
| 185 | +| 哈希表 | key 到 value 的映射 | 平均 $O(1)$ 查询 | |
| 186 | +| LRU Cache | 最近访问结果 | 减少重复 API / 模型调用 | |
| 187 | +| KV Cache | 历史 token 的 Key/Value | 降低自回归解码成本 | |
| 188 | +| 前缀缓存 | prompt 前缀对应状态 | 复用相同系统提示和上下文 | |
| 189 | +| DP 表 | 子问题结果 | 避免指数级重复递归 | |
| 190 | +| 向量索引 | embedding 的图/聚类结构 | 加速相似搜索 | |
| 191 | + |
| 192 | +风险: |
| 193 | + |
| 194 | +- 缓存会占内存。 |
| 195 | +- 缓存可能过期。 |
| 196 | +- 分布变化会降低命中率。 |
| 197 | +- 多租户场景要避免数据串线。 |
| 198 | +- 预计算可能增加更新成本。 |
| 199 | + |
| 200 | +AI 系统中最常见的答案是:缓存适合高复用、低变化、计算昂贵的数据;不适合敏感、频繁变化或难以失效的数据。 |
| 201 | + |
| 202 | +--- |
| 203 | + |
| 204 | +<h1 id="q-009">3. 常用数据结构如何分类?</h1> |
| 205 | + |
| 206 | +常用数据结构可以按组织方式分类: |
| 207 | + |
| 208 | +1. **线性结构** |
| 209 | + |
| 210 | + 数组、链表、栈、队列、双端队列、字符串。 |
| 211 | + |
| 212 | +2. **映射结构** |
| 213 | + |
| 214 | + 哈希表、集合、LRU、LinkedHashMap。 |
| 215 | + |
| 216 | +3. **树形结构** |
| 217 | + |
| 218 | + 二叉树、二叉搜索树、红黑树、堆、Trie、线段树、树状数组。 |
| 219 | + |
| 220 | +4. **图结构** |
| 221 | + |
| 222 | + 邻接表、邻接矩阵、DAG、计算图、知识图谱。 |
| 223 | + |
| 224 | +5. **高级检索结构** |
| 225 | + |
| 226 | + 跳表、位图、布隆过滤器、倒排索引、向量索引。 |
| 227 | + |
| 228 | +6. **算法思想** |
| 229 | + |
| 230 | + 递归、分治、二分、回溯、动态规划、贪心、滑动窗口、双指针。 |
| 231 | + |
| 232 | +<h2 id="q-010">面试问题:数组、链表、栈、队列、哈希表、树、图分别适合什么场景?</h2> |
| 233 | + |
| 234 | +| 结构 | 特点 | 高频场景 | |
| 235 | +| --- | --- | --- | |
| 236 | +| 数组 | 连续内存,随机访问快 | Tensor、batch、embedding table | |
| 237 | +| 链表 | 插入删除灵活,随机访问慢 | LRU 双向链表、内存块管理 | |
| 238 | +| 栈 | 后进先出 | 递归、DFS、表达式解析、单调栈 | |
| 239 | +| 队列 | 先进先出 | BFS、任务调度、DataLoader、消息系统 | |
| 240 | +| 哈希表 | 平均 $O(1)$ 查询 | vocab、特征缓存、metadata | |
| 241 | +| 树 | 层级和有序结构 | AST、Trie、堆、索引结构 | |
| 242 | +| 图 | 表示关系和依赖 | 计算图、知识图谱、workflow、GNN | |
| 243 | + |
| 244 | +AI 面试中常见追问: |
| 245 | + |
| 246 | +- 为什么 Tensor 更像数组而不是链表? |
| 247 | +- 为什么 tokenizer 词表适合哈希表? |
| 248 | +- 为什么计算图是 DAG? |
| 249 | +- 为什么 RAG 索引不是简单数组遍历? |
| 250 | + |
| 251 | +<h2 id="q-011">面试问题:哪些数据结构在大模型系统中最常见?</h2> |
| 252 | + |
| 253 | +大模型系统常见结构: |
| 254 | + |
| 255 | +- **Tensor / 多维数组**:模型参数、激活、embedding、attention score。 |
| 256 | +- **KV Cache**:保存每层历史 token 的 key/value。 |
| 257 | +- **Ring Buffer / Queue**:流式 token、请求队列、日志队列。 |
| 258 | +- **HashMap**:token 到 id、request id 到状态、session id 到上下文。 |
| 259 | +- **Trie / Radix Tree**:tokenizer、prefix cache、约束解码。 |
| 260 | +- **Heap**:Top K、beam search、优先级调度。 |
| 261 | +- **DAG**:计算图、workflow、Agent task graph。 |
| 262 | +- **Vector Index**:RAG、推荐、图像检索。 |
| 263 | +- **Bitmap / Bloom Filter**:快速去重、过滤已见文档或 token。 |
| 264 | + |
| 265 | +面试中可以把它们串成一个 LLM 推理链路: |
| 266 | + |
| 267 | +```text |
| 268 | +Prompt -> Tokenizer Trie/Vocab HashMap -> Tensor -> KV Cache -> Heap TopK -> Stream Queue -> Session Store |
| 269 | +``` |
| 270 | + |
| 271 | +<h2 id="q-012">面试问题:刷题中的数据结构如何迁移到真实 AI 项目?</h2> |
| 272 | + |
| 273 | +刷题训练的是抽象能力,真实项目需要把抽象能力落到工程场景。 |
| 274 | + |
| 275 | +| 刷题知识 | AI 项目迁移 | |
| 276 | +| --- | --- | |
| 277 | +| 二分查找 | 阈值搜索、学习率区间、召回分数边界 | |
| 278 | +| 堆 | Top K token、候选召回、任务优先级 | |
| 279 | +| 哈希表 | 特征缓存、token vocab、请求状态 | |
| 280 | +| 图搜索 | 计算图、知识图谱、Agent workflow | |
| 281 | +| 动态规划 | 编辑距离、序列对齐、Viterbi、CTC | |
| 282 | +| Trie | tokenizer、敏感词过滤、约束解码 | |
| 283 | +| LRU | KV Cache、RAG cache、模型服务缓存 | |
| 284 | +| 单调队列 | 滑动窗口最大值、时序特征聚合 | |
| 285 | + |
| 286 | +高分回答不是“我会刷题”,而是能解释某个数据结构在实际系统中为什么能降低复杂度、降低延迟或提升可维护性。 |
| 287 | + |
| 288 | +--- |
| 289 | + |
| 290 | +## 高频速记 |
| 291 | + |
| 292 | +1. 数据结构决定数据如何组织,算法决定数据如何处理。 |
| 293 | +2. AI 系统中的 Tensor、KV Cache、计算图、向量索引都离不开数据结构。 |
| 294 | +3. 复杂度分析直接影响模型训练、推理、检索和部署成本。 |
| 295 | +4. 平均复杂度不等于生产可靠性,尾延迟和最坏情况也要关注。 |
| 296 | +5. 数组随机访问快,链表插删灵活,哈希表平均查询快,树适合有序和层级,图适合关系和依赖。 |
| 297 | +6. 空间换时间在 AI 中非常常见,例如 KV Cache、LRU、DP、向量索引。 |
| 298 | +7. 刷题知识要迁移到真实场景,例如 Top K、RAG、tokenizer、DataLoader、workflow。 |
| 299 | + |
| 300 | +## 参考资料 |
| 301 | + |
| 302 | +- Wang Zheng, [**数据结构和算法必知必会的50个代码实现**](https://github.com/wangzheng0822/algo). |
| 303 | +- 《剑指 Offer》与 LeetCode 高频算法题。 |
| 304 | +- PyTorch / TensorFlow 中 Tensor、DataLoader、计算图相关工程实践。 |
0 commit comments