题目
题目来源:力扣(LeetCode
给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K 。
返回到目标结点 target 距离为 K 的所有结点的值的列表。 答案可以以任何顺序返回。
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 7,4,以及 1
注意,输入的 “root” 和 “target” 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。
思路分析
解法一: 深度优先搜索 + 反向设置深度
- 找距离 target 为 k 的节点,其实就是找 以 target 为根节点,深度为 k 的节点;
- 首先使用深度优先搜索,找到目标节点 target,然后将 target 作为根节点,从 target 出发,使用深度优先搜索去寻找与 target 距离为 k 的所有节点,即深度为 k 的所有节点;
- 通常,node 每向上回溯一层,DFS 的限制深度就会减一,直至根节点,深度变为 0 (树从根结点开始往下数,叶子结点所在的最大层数称为 树的深度);
我们反过来想,将 target 节点的深度置为 k,那么以 target 为根节点的树中,每向下一层,深度 减一,直至深度为 0 的节点,就是我们要找的节点。
/*** Definition for a binary tree node.* function TreeNode(val) {* this.val = val;* this.left = this.right = null;* }*//*** @param {TreeNode} root* @param {TreeNode} target* @param {number} k* @return {number[]}*/var distanceK = function(root, target, k) {if (!root) return [];let targetNode = null;let res = [];// 在递归过程中,存储已访问过的节点let paths = [];// 找到 target 节点,将其存储到 targetNode 中findTarget(root, target);// 以target 节点为根节点,向下寻找距离为 k 的节点getDownDis(targetNode, k);// 以 target 为根节点,向上寻找距离为 k 的节点while(targetNode.parent && k > 0) {targetNode = targetNode.parent;getDownDis(targetNode, --k);}function findTarget(root, target) {if (!root || targetNode) return;// 找到 target 节点,存储到 targetNode 中if (root.val === target.val) {targetNode = root;}// 从左子树中找 target 节点if (root.left) {// 记录左孩子的父节点root.left.parent = root;findTarget(root.left, target);}// 从右子树中找 target 节点if (root.right) {// 记录右孩子的父节点root.right.parent = root;findTarget(root.right, target);}}// node每向上回溯一层,DFS的限制深度就会减1,直到根节点root。若target在树中的深度为h, 且位于左子树中, 那么最后一次DFS就是在root与其右子树部分找出深度为k-h的所有节点// 寻找离目标节点 target 的距离为 k 的节点:那么以 target 为根的树中,深度为 k 的所有节点显然符合要求function getDownDis(node, k) {// 已访问过的节点,不再访问if (node === null || paths.indexOf(node) !== -1) return;// 将未访问过的节点存储到 paths 中paths.push(node);if (k > 0) {// 通常,node 每向上回溯一层,DFS 的限制深度就会减一,直至根节点,深度变为 0 (树从根结点开始往下数,叶子结点所在的最大层数称为 树的深度)// 我们反过来想,将 target 节点的深度置为 k,那么以 target 为根节点的树中,每向下一层,深度 减一,直至深度为 0 的节点,就是我们要找的节点getDownDis(node.left, k - 1);getDownDis(node.right, k - 1);} else if (k === 0) {res.push(node.val);}}return res;};
解法二:深度优先搜索 + 哈希表
- 我们将 target 作为根节点,从 target 出发,使用深度优先搜索寻找与 target 距离为 k 的所有节点,即深度为 k 的所有节点
- 由于输入的二叉树没有记录父节点,因此,我们从根节点 root 出发,使用深度优先搜索遍历整棵树,同时用一个哈希表记录每个节点的父节点
- 然后从 target 出发,使用深度优先搜索遍历整棵树,除了搜索左右子树外,还可以顺着父节点向上搜索
- 由于每个节点的值都是唯一的,因此哈希表的键可以用节点值代替。此外,为了避免在深度优先搜索时重复访问节点,递归是额外传入来源节点 from,在递归前比较目标节点是否与来源节点相同,不同的情况下才进行递归
/*** Definition for a binary tree node.* function TreeNode(val) {* this.val = val;* this.left = this.right = null;* }*//*** @param {TreeNode} root* @param {TreeNode} target* @param {number} k* @return {number[]}*/var distanceK = function(root, target, k) {const parents = new Map();const ans = [];const findParents = (node) => {// DFS 左子树,记录每个节点的父节点if (node.left != null) {parents.set(node.left.val, node);findParents(node.left);}// DFS 右子树,记录每个节点的父节点if (node.right != null) {parents.set(node.right.val, node);findParents(node.right);}}// 从 root 出发,深度优先搜索,记录每个节点的父节点findParents(root);const findAns = (node, from, depth, k) => {if (node == null) return;// 找到距离为 k 的节点if (depth === k) {ans.push(node.val);return;}// 在左子树中寻找距离为k (即深度为k) 的节点if (node.left !== from) {findAns(node.left, node, depth + 1, k);}// 在右子树中寻找距离为k (即深度为k) 的节点if (node.right !== from) {findAns(node.right, node, depth + 1, k);}// 顺着父节点向上寻找距离为k (即深度为k) 的节点if (parents.get(node.val) !== from) {findAns(parents.get(node.val), node, depth + 1, k);}}// 以 target 为根节点,从 target 出发,寻找所有深度为 k 的节点,根节点 target 的深度为 0findAns(target, null, 0, k);return ans;};
