You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树结构中q次最短路径查询后求访问量最高节点的最优算法咨询

解决树路径查询访问次数最多节点的最优算法

嗨,我完全懂你遇到的困境——直接用BFS遍历每条查询路径在数据量到1e5的时候肯定会超时,毕竟单次查询的路径长度最坏能到O(n),总操作数会飙升到O(q*n),这完全扛不住。这里有个O(n log n + q log n)的最优解法,核心是用差分思想结合LCA(最近公共祖先),绝对能搞定1e5级别的规模。

核心思路

树中任意两点s到t的最短路径,都可以拆成s -> LCA(s,t)和t -> LCA(s,t)(注意LCA只被统计一次)。我们可以用差分数组批量标记所有查询的路径,最后通过一次树的遍历统计每个节点的实际访问次数,不用逐个遍历每条路径。

具体步骤

1. 预处理LCA(倍增法)

首先得预处理每个节点的最近公共祖先,这样每次查询能在O(log n)时间内得到s和t的LCA。倍增法是最常用的高效方式:

  • 预处理每个节点的深度depth[u],以及up[k][u](表示u的2^k级祖先)
  • 预处理的时间复杂度是O(n log n),单次LCA查询的时间是O(log n)

2. 初始化差分数组

创建一个大小为n+1的数组cnt,初始值全为0。这个数组用来记录路径的差分标记。

3. 处理每一次查询

对于每个查询的源节点s和目标节点t:

  • 计算l = LCA(s, t)
  • 执行以下差分操作:
    cnt[s] += 1
    cnt[t] += 1
    cnt[l] -= 1
    if parent[l] exists:
        cnt[parent[l]] -= 1
    

这么做的逻辑是:

  • cnt[s] +=1标记从s到根的路径整体加1
  • cnt[t] +=1标记从t到根的路径整体加1
  • cnt[l] -=1和cnt[parent[l]] -=1是减去两次从l到根的路径(因为s到根和t到根的路径在l到根部分重复了),同时保证l本身只被计数一次。

4. 后序遍历统计实际访问次数

通过一次后序遍历(迭代式DFS更稳妥,避免递归栈溢出),对于每个节点u,将所有子节点的cnt值累加到u的cnt上。操作完成后,cnt[u]就是节点u在所有查询中被访问的总次数。

5. 找出访问次数最多的节点

遍历所有节点,找到cnt值最大的那个节点即可(如果有多个,任选其一都满足要求)。

复杂度分析

  • LCA预处理:O(n log n)
  • 处理q次查询:O(q log n)
  • 后序遍历统计:O(n)
    总复杂度是O(n log n + q log n),完全能处理n和q都为1e5的场景。

为什么BFS会超时?

BFS遍历每条路径的方式,最坏情况下每次查询要走O(n)个节点,q=1e5时总操作数会达到1e10,远远超出时间限制。而上面的方法把路径标记转化为O(1)的差分操作,最后用一次遍历统计,效率提升了几个数量级。

实现细节提醒

  • 递归DFS处理1e5节点可能会栈溢出,建议用迭代式DFS或者逆序BFS来做后序遍历。
  • 倍增法的预处理要注意数组大小,比如up数组的维度可以设为[20][n+1](因为2^20已经超过1e5)。

内容的提问来源于stack exchange,提问作者joydip panda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:59:22