你在做代码迁移,需要把一个递归实现的树形权限校验函数改成迭代版本,因为线上出现了 StackOverflowError。原函数大概是这样:check(node) { if (node == null) return true; for (child : node.children) if (!check(child)) return false; return node.hasPermission; } 这棵树在生产环境最深有 50 万层(由于历史数据异常),平均分支因子 1.2。团队有人提出:把它改成显式栈的迭代版本,时间复杂度不变,空间复杂度从 O(depth) 递归栈变成 O(depth) 显式栈,所以没省内存。作为负责人,你怎么分析这个递归的复杂度和实际内存?为什么显式栈可能仍然解决问题?如果改成迭代后仍然 OOM,你怎么办?
面试官在考什么
考察能否把递归复杂度分析落到调用栈限制、堆内存和实际数据异常上,并给出从代码到数据治理的决策。
回答框架 · 前两步
- 递归时间复杂度 O(n)(n 为节点数),空间复杂度 O(d)(d 为树深);原题 d=50 万,远超 JVM 默认线程栈大小(通常 512KB-1MB),每帧几百字节,必然 StackOverflowError。
- 显式栈迭代版时间复杂度仍 O(n),空间复杂度仍 O(d),但栈在堆上,容量远大于调用栈,且可控制每帧存储内容,因此可能避免溢出。
还有 3 条答案要点、 3 层追问、 4 个常见坑, 以及针对你简历的追问路径,在定制报告里。
延伸阅读
想看这个方向的整体准备路线? 进入「数据结构与算法」方向页
同一能力域的其他题
- 下面这段代码用于统计数组中和为某值的下标对: ```python def pairs(nums): s = set(nums) res = [] for i in range(len(nums)): for j in range(i+1, len(nums)): if nums[i] + nums[j] in s: res.append((i, j)) return res ``` (1) 它的时间复杂度和空间复杂度分别是多少?请说明每一项是怎么算出来的。 (2) 如果线上 nums 的长度 n = 10^6,这个函数能不能直接上线?为什么? (3) 如果 nums 中存在大量重复元素,复杂度会变吗?
- 有一个去重任务:内存中一次性有 1 亿条 URL,平均长度 80 个 ASCII 字符,要在一台 JVM 堆上限 4GB 的机器上做「精确去重」(不允许有误判,不允许落盘到外部存储作为主方案)。有人提议直接用 HashSet<String> 装全部 URL。请回答:(1) 估算这个 HashSet 的内存占用,判断是否可行,要求给出量级估算的过程而不只是结论;(2) 如果不可行,写出你实际会采用的方案,并说明它把成本转移到了哪里。
- 你负责一个日处理 50 亿条日志的离线去重系统。输入是 50 亿个 128-bit 的 ID,要求输出去重后的 ID 集合。团队提出两个方案:方案 A 用哈希表,期望 O(n) 时间和 O(n) 额外空间,单机 256 GB 内存可能放不下;方案 B 先外部排序再扫描去重,O(n log n) 时间和 O(1) 额外空间(磁盘),但需要多轮磁盘 I/O。作为技术负责人,你怎么做复杂度分析和工程选型?请给出量化估算和最终决策,并说明你会怎么向团队解释 Big-O 与实际吞吐之间的关系。
- 阅读下面的 Python 函数,n 是正整数。请分析它的时间复杂度(以 total += 1 的执行次数为准)和额外空间复杂度,并说出推导过程。 ```python def h(n): total = 0 i = 1 while i <= n: for j in range(i): total += 1 i *= 2 return total ```
- Python 的 `list.append` / C++ 的 `vector::push_back` 在容量不足时会申请更大的内存并搬移已有元素。以「容量满时容量翻倍」的策略为例: (1) 单次 append 的最坏时间复杂度是多少? (2) 为什么文档和面试里普遍说 append 是 O(1)?请把 n 次连续 append 的总代价推出来。 (3) 如果把策略换成「每次容量不足时只加 1 个位置」,结论怎么变? (4) 为什么工程实现里常见 ×2 或 ×1.5,而不是 ×1.1 或固定增量?
- 一个动态数组从容量 1 开始,每次写满就扩容为原来的 2 倍,并把旧元素全部拷贝到新数组。现在连续 append 100 万次(元素总数为 100 万)。请回答:(1) 整个过程总的元素拷贝次数是多少?(2) 单次 append 的最坏代价和均摊代价分别是多少?(3) 如果产品要求「任意单次操作的 P99 延迟抖动不能超过某个阈值」,这个 2 倍扩容策略有什么问题,你会怎么改?