1. 问题描述:
给定一个二叉树(具有根结点 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" 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。
提示:
给定的树是非空的。
树上的每个结点都具有唯一的值 0 <= node.val <= 500 。
目标结点 target 是树上的结点。
0 <= K <= 1000.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/all-nodes-distance-k-in-binary-tree
2. 思路分析:
① 这是一道经典的关于二叉树搜索的题目,所以可以使用深度优先搜索(dfs)和广度优先搜索(bfs)来解决,因为是要求解出所有与目标节点target为K的节点,所以比较容易想到的是广度优先搜索,因为广度优先搜索可以按照层次进行搜索,当发现队列中弹出的节点的深度为K的时候此时队列中的所有节点与目标节点的距离都是K(这一点很重要,我们可以利用广度优先搜索的这个特点应用到其他类似的题目),但是由于是二叉树,所以我们只知道当前节点的左孩子与右孩子,并不知道当前节点的父节点,所以假如使用广度优先搜索解决的话我们需要知道当前节点的父节点才可以对当前节点所有能够连接到的节点进行广度优先搜索,对于这个问题我们完全可以使用dfs搜索来记录当前节点的父节点,我们可以使用dfs搜索二叉树,在方法中传递当前节点的父节点,这样可以在递归到当前节点的时候就可以为当前的节点添加父节点了,具体的步骤:
a :使用dfs方法使当前节点添加一个父节点
b:使用bfs方法搜索当前节点的周围节点,也就是左孩子、右孩子与父节点这样就可以使得距离不断逼近K,当发现队列中弹出的元素的距离为K的这个时候队列中的节点都是距离目标节点距离为K的所以我们将这些节点的值加入到结果集即可
② 除了第①种方法之外,力扣还提供了一种纯dfs解决的方法,感觉思路也是挺好的,主要是利用递归层层返回的特点,这样就可以返回到上一层节点添加与父节点有联系的距离目标节点为K的所有节点,每返回到上一层节点的时候那么就计算当前这一层下对应的其余满足条件的节点,下面是一个具体的例子:
假如我们需要找距离节点4距离为3的所有节点,我们可以从3这个根节点出发,递归左子树与右子树,发现最后到4这个节点的时候才是目标节点,我们找到目标节点为了方便后面的处理dfs方法可以返回一个整数,表示当前的节点距离目标节点的距离,找到之后返回2这个节点,而4是2的右孩子节点所以我们在返回到2这个节点的时候应该找2这个节点的左子树往下寻找,并且这个时候距离为2,往下寻找一直到距离为K的节点(每往下一层深度都是加1的)并且将寻找到的距离为K的所有节点加入到结果集中,可以使用一个辅助的方法来求解当前距离为d的情况下距离为K的所有节点,dfs方法具有返回值的好处是能够判断出找到目标节点之后层层返回的时候dfs返回的下一层的节点属于左孩子还是右孩子,假如下一层节点是左孩子返回到当前节点的时候就需要在当前节点的右子树中寻找,否则相反,例如在寻找目标节点为4的时候由节点2返回到节点节点5而节点2是右子树所以这个时候返回到5这个节点的时候需要在5的左子树中寻找,所以这个步骤如下:
先找到目标节点,然后计算目标节点下左右子树中距离为K的所有节点并且将他们累加到结果集中,利用递归层层返回的特点并且根据下一层节点属于左孩子还是右孩子找到上一层节点的另外一个子树距离为K的所有节点,这两部分节点就为结果集中的答案
3. 代码如下:
dfs + bfs:
import collections
from typing import List
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 存储当前节点的父节点
def dfs(self, root: TreeNode, parent: TreeNode):
if not root: return
root.parent = parent
self.dfs(root.left, root)
self.dfs(root.right, root)
def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]:
self.dfs(root, None)
rec = {target}
queue = collections.deque()
queue.append((target, 0))
while queue:
if queue[0][1] == K:
# 当发现第一个距离为K的节点的时候队列中剩下的节点都是距离为K的
return [node.val for node, dis in queue]
# 相当于三个平行状态
poll, d = queue.popleft()
for node in (poll.left, poll.right, poll.parent):
if node and node not in rec:
rec.add(node)
queue.append((node, d + 1))
return list()
dfs:
from typing import List
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 当前距离为d的情况下求解往下距离为K的节点
def addNode(self, root: TreeNode, d: int, K: int, res: List[int]):
if not root:
return
elif d == K:
res.append(root.val)
else:
self.addNode(root.left, d + 1, K, res)
self.addNode(root.right, d + 1, K, res)
def dfs(self, root: TreeNode, res: List[int], target: TreeNode, K: int):
if not root:
return -1
# 当前节点等于目标节点
elif root == target:
# 将当前节点的左右子树距离为K的节点加入到结果集中
self.addNode(root, 0, K, res)
# 距离为1
return 1
else:
# dfs具有一个返回值用来标记是返回结果属于左孩子还是属于右孩子
L, R = self.dfs(root.left, res, target, K), self.dfs(root.right, res, target, K)
if L != -1:
# 说明返回的节点属于左孩子节点, 否则属于右孩子节点
# 当属于左孩子节点的时候那么应该在当前这一层节点的右子树中寻找
if L == K: res.append(root.val)
self.addNode(root.right, L + 1, K, res)
return L + 1
elif R != -1:
if R == K: res.append(root.val)
self.addNode(root.left, R + 1, K, res)
return R + 1
else:
return -1
def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]:
res = list()
self.dfs(root, res, target, K)
return res