亚马逊面试DAG问题优化思路求教:遍历法存疑
突破DAG“最吵闹的人”面试题瓶颈的实用思路
嘿,太懂这种卡在亚马逊面试题上的挠头感了!你提到的用传递性遍历每个节点路径找终点、再比较筛选的思路方向是对的,但肯定是碰到效率或者逻辑上的瓶颈了对吧?我来给你捋捋可能的问题和优化方案:
你当前思路的核心瓶颈
- 时间复杂度爆炸:如果DAG节点数稍多,比如超过20个,遍历所有路径的最坏时间复杂度会达到
O(2^n),这在面试里绝对会被面试官追问优化方向——毕竟实际场景里没人能接受这么低效的解法。 - 重复计算冗余:不同节点的路径大概率有大量重叠的子路径,你现在的方法应该是每次遍历都从头开始,完全没复用已经计算过的结果,这就导致做了很多无用功。
优化方案:拓扑排序+动态规划(DAG的黄金组合)
既然是有向无环图,拓扑排序是绕不开的高效工具,结合动态规划可以彻底解决重复计算的问题,把时间复杂度降到线性级别:
- 定义状态:给每个节点维护一个集合:
reachable_nodes[u]:节点u能到达的所有节点(如果“最吵闹的人”定义为能影响最多人)- 要是题目定义是被最多人能影响,就改成维护
reach_to[u]:所有能到达u的节点
- 拓扑序遍历:
- 先对DAG做拓扑排序,然后逆序遍历拓扑序列(计算
reachable_nodes时):
对于节点u,遍历它的所有邻接节点v,直接把reachable_nodes[v]的所有节点合并到reachable_nodes[u]里,再加上v本身——相当于直接复用后续节点的计算结果,不用再重复走路径。
- 先对DAG做拓扑排序,然后逆序遍历拓扑序列(计算
- 筛选目标节点:
- 遍历所有节点的集合长度,找到长度最大的那个节点就是“最吵闹的人”。
举个直观例子
假设DAG是A → B → C,同时A → C:
- 拓扑序是
A, B, C - 逆序遍历
C:reachable_nodes[C] = {C} - 遍历
B:reachable_nodes[B] = {B} ∪ reachable_nodes[C] = {B, C} - 遍历
A:reachable_nodes[A] = {A} ∪ reachable_nodes[B] ∪ reachable_nodes[C] = {A, B, C} - 此时
A的可达节点最多,就是我们要找的“最吵闹的人”
简化代码思路
用Python写的伪代码大概是这样:
from collections import deque def find_loudest_person(graph): # 第一步:生成拓扑序列 in_degree = {node: 0 for node in graph} for u in graph: for v in graph[u]: in_degree[v] += 1 queue = deque([node for node in in_degree if in_degree[node] == 0]) topo_order = [] while queue: current = queue.popleft() topo_order.append(current) for neighbor in graph[current]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: queue.append(neighbor) # 第二步:逆序拓扑序计算可达节点集合 reachable = {node: {node} for node in graph} for node in reversed(topo_order): for neighbor in graph[node]: reachable[node].update(reachable[neighbor]) # 第三步:找到可达节点最多的人 loudest = max(graph.keys(), key=lambda x: len(reachable[x])) return loudest
这个解法的时间复杂度是O(V+E)(V是节点数,E是边数),比你之前的路径遍历高效太多,完全能应对面试里的测试用例。
内容的提问来源于stack exchange,提问作者Priyanka Sinha




