[路飞]_前端算法第一零六弹-二叉树中所有距离为 K 的结点

88 阅读2分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K

返回到目标结点 target 距离为 K 的所有结点的值的列表。 答案可以以任何顺序返回。

示例 1:

图片.png

输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 74,以及 1
注意,输入的 "root""target" 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。

深度优先搜索 + 哈希表

若将 targettarget 当作树的根结点,我们就能从 targettarget 出发,使用深度优先搜索去寻找与 targettarget 距离为 k 的所有结点,即深度为 k 的所有结点。

由于输入的二叉树没有记录父结点,为此,我们从根结点 root 出发,使用深度优先搜索遍历整棵树,同时用一个哈希表记录每个结点的父结点。

然后从 targettarget 出发,使用深度优先搜索遍历整棵树,除了搜索左右儿子外,还可以顺着父结点向上搜索。

代码实现时,由于每个结点值都是唯一的,哈希表的键可以用结点值代替。此外,为避免在深度优先搜索时重复访问结点,递归时额外传入来源结点 fromfrom,在递归前比较目标结点是否与来源结点相同,不同的情况下才进行递归。

var distanceK = function(root, target, k) {
    const parents = new Map();
    const ans = [];

    const findParents = (node) => {
        if (node.left != null) {
            parents.set(node.left.val, node);
            findParents(node.left);
        }
        if (node.right != null) {
            parents.set(node.right.val, node);
            findParents(node.right);
        }
    }

    // 从 root 出发 DFS,记录每个结点的父结点
    findParents(root);

    const findAns = (node, from, depth, k) => {
        if (node == null) {
            return;
        }
        if (depth === k) {
            ans.push(node.val);
            return;
        }
        if (node.left !== from) {
            findAns(node.left, node, depth + 1, k);
        }
        if (node.right !== from) {
            findAns(node.right, node, depth + 1, k);
        }
        if (parents.get(node.val) !== from) {
            findAns(parents.get(node.val), node, depth + 1, k);
        }
    }
    // 从 target 出发 DFS,寻找所有深度为 k 的结点
    findAns(target, null, 0, k);

    return ans;
};

复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 是二叉树的结点个数。需要执行两次深度优先搜索,每次的时间复杂度均为 O(n)O(n)
  • 空间复杂度:O(n)O(n)。记录父节点需要 O(n)O(n) 的空间,深度优先搜索需要 O(n)O(n) 的栈空间。

图片.png