在问 · 算法题解

863. 二叉树中所有距离为 K 的结点

中等二叉树父指针表 + 广度优先搜索力扣原题 ↗

题目描述

输入二叉树的根节点 root、树中某个目标节点 target 和整数 k。树用层序数组表示,空位为 null;target 用节点值给出,树中节点值互不相同。

两个节点的距离是它们之间路径上的边数,路径既可以向下走到孩子,也可以向上走到父节点。

返回所有与 target 距离恰好为 k 的节点的值,顺序不限。没有这样的节点时返回空数组。

示例 1
输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, k = 2
输出:[7,4,1]

解释:节点 5 的两个孙子 7、4 距离为 2,向上经过根 3 到达的节点 1 距离也为 2。

示例 2
输入:root = [1], target = 1, k = 3
输出:[]

解释:树只有一个节点,不存在距离为 3 的节点。

数据范围

  • 节点数在 [1, 500] 范围内。
  • 0 <= Node.val <= 500
  • 所有 Node.val 互不相同。
  • target 是树中的节点。
  • 0 <= k <= 1000

解题思路:父指针表 + 广度优先搜索

树只能从父走向子,但距离可以往上走。先 DFS 一遍记下每个节点的父节点,树就变成了无向图:每个节点有左、右、父三个邻居。然后从 target 出发做 BFS,扩展 k 层,第 k 层的节点就是答案。用 seen 防止沿着刚走过的边走回去。

为什么正确

第 i 轮扩展后,frontier 恰好是与 target 距离为 i 的所有节点,seen 是距离 <= i 的所有节点。

复杂度分析

时间 O(n),空间 O(n)(父指针表、seen 和队列)。

易错点

只向下搜索会漏掉经过父节点的路径(例如示例里的 1);加了父指针后忘记 seen 又会在父子之间来回走,得到距离错误的节点。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
        parent = {}
        def dfs(node, par):
            if node:
                parent[node] = par
                dfs(node.left, node)
                dfs(node.right, node)
        dfs(root, None)
        seen = {target}
        frontier = [target]
        for _ in range(k):
            nxt = []
            for node in frontier:
                for nb in (node.left, node.right, parent[node]):
                    if nb and nb not in seen:
                        seen.add(nb)
                        nxt.append(nb)
            frontier = nxt
        return [node.val for node in frontier]

载入编辑器,自己跑一遍 →

其他解法

父指针法把树变成无向图,直接套广度优先模板;距离递归法利用树的结构在回溯时分类收集,省掉哈希表,但推理更细。

解法 2:递归返回到 target 的距离 时间 O(n),空间 O(h)

先写辅助函数 collect(node, d),把 node 子树中深度恰好为 d 的节点加入答案。主递归 dfs(node) 返回 node 到 target 的距离(target 不在子树中返回 -1):遇到 target 时调用 collect(target, k) 并返回 0;如果 target 在左子树且距离为 d,那么当 d = k 时当前节点就是答案,否则到右子树里收集深度为 k - d - 1 的节点;右子树同理。

不需要父指针表和 seen 集合,除答案外只用递归栈。但要把“向上走再拐到另一侧”的距离算清楚,分类讨论比把树当无向图做广度优先更容易出错。

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
        ans = []

        def collect(node, d):
            if node is None or d < 0:
                return
            if d == 0:
                ans.append(node.val)
                return
            collect(node.left, d - 1)
            collect(node.right, d - 1)

        def dfs(node):
            if node is None:
                return -1
            if node is target:
                collect(node, k)
                return 0
            for child, other in ((node.left, node.right), (node.right, node.left)):
                d = dfs(child)
                if d != -1:
                    d += 1
                    if d == k:
                        ans.append(node.val)
                    else:
                        collect(other, k - d - 1)
                    return d
            return -1

        dfs(root)
        return ans

二叉树 专题的其他题目

查看 二叉树 全部题目 →