第 3 章 数据结构与算法:高频问题

本章重点:排序、哈希、树、图是微服务面试中手写代码的重灾区。面试官不仅考察实现正确性,更关注复杂度分析、边界处理与工程优化能力。大厂倾向在 20 分钟内完成一道中等难度题,并追问优化路径。
读题确认需求 暴力解法分析 优化思路推导 复杂度分析 手写代码 测试边界用例 边界确认 → 递进分析 → 时空权衡 正常/异常/大数

📌 面试问题 1:快速排序

面试官考察点分治思想理解、原地排序、平均/最差时间复杂度、稳定性认知、工程优化意识。
回答框架1️⃣ 选 pivot → 2️⃣ 分区(双指针交换)→ 3️⃣ 递归左右子数组 → 4️⃣ 复杂度 O(n log n) / O(n²)。
简短示例回答“快速排序采用分治策略,随机选择 pivot 后通过 hoare 分区将数组分为小于和大于 pivot 两部分,平均时间复杂度 O(n log n),最差 O(n²)(有序时)。手写时注意边界判断和递归深度。”
可能追问• 如何避免最差情况?
• 快速排序是否稳定?
• 与 std::sort 的差异?
• 工程中何时改用插入排序?
避坑提醒⚠️ 分区时指针移动条件易写错导致死循环;必须考虑相等元素处理;递归栈溢出需转为迭代或尾递归优化。

📌 面试问题 2:手写哈希表(链地址法)

面试官考察点哈希函数设计、冲突解决(链地址 vs 开放地址)、扩容 rehash、负载因子、C++ 内存管理。
回答框架1️⃣ 定义桶数组(vector<list>)→ 2️⃣ 哈希函数取模 → 3️⃣ insert/search/delete → 4️⃣ 负载因子 >0.75 时扩容 ×2 并 rehash。
简短示例回答“哈希表核心是数组+链表,用 std::hash 取模定位桶,冲突时头插。插入 O(1) 平均,最差 O(n)。实现时注意迭代器失效和异常安全,扩容时重新分配所有元素。”
可能追问• 为什么链表不直接用 vector?
• 如何优化缓存行?
• 开放地址法优缺点?
• C++ 中 std::unordered_map 如何实现?
避坑提醒⚠️ 忘记重载哈希函数导致编译错误;扩容时原迭代器失效;负载因子过高引发性能雪崩。

📌 面试问题 3:二叉树最近公共祖先(LCA)

面试官考察点递归理解、后序遍历、BST 优化、二叉搜索树性质利用、空间复杂度。
回答框架1️⃣ 递归后序遍历 → 2️⃣ 左子树含 p 返回左,右子树含 q 返回右 → 3️⃣ 若左右均非空,当前节点即 LCA → 4️⃣ BST 版本按值比较大小决定方向。
简短示例回答“对普通二叉树使用 DFS 后序递归:查找 p 或 q,如果左右子树均找到则当前节点为 LCA。时间复杂度 O(n),空间 O(h)。若为 BST,利用节点值特性可降至 O(log n)。”
可能追问• 如果节点可能不存在?
• 如何实现非递归版本?
• 如何处理多个节点 LCA?
• 深度与节点数关系?
避坑提醒⚠️ 递归时未正确传递 bool 返回值;未处理空树或单节点;BST 版本忽略值相等的情况。
快速排序 稳定性? 最坏情况 O(n²) 随机 pivot 三路快排优化 小数组插排 std::sort 混合 面试官追问方向

🧠 高频算法复杂度速查表

算法类别 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 手写频率
快速排序O(n log n)O(n²)O(log n)否★★★★★
归并排序O(n log n)O(n log n)O(n)是★★★★☆
堆排序O(n log n)O(n log n)O(1)否★★★★☆
二分查找O(log n)O(log n)O(1)—★★★★☆
哈希表(链地址)O(1) 均摊O(n)O(n)—★★★★★
二叉树遍历O(n)O(n)O(h)—★★★★☆
图 DFS / BFSO(V+E)O(V+E)O(V)—★★★★☆
💡 加分项提示:回答任何算法题时,主动先问输入规模(数据量)、是否允许额外空间;手写代码后补充测试用例(空、单元素、重复元素、大数据)。大厂面试官看重“能写出 bug‑free 生产级代码”的习惯。