1. 问题描述:
给定二叉树,按垂序遍历返回其结点值。对位于 (X, Y) 的每个结点而言,其左右子结点分别位于 (X-1, Y-1) 和 (X+1, Y-1)。
把一条垂线从 X = -infinity 移动到 X = +infinity ,每当该垂线与结点接触时,我们按从上到下的顺序报告结点的值( Y 坐标递减)。
如果两个结点位置相同,则首先报告的结点值较小。按 X 坐标顺序返回非空报告的列表。每个报告都有一个结点值列表。
示例 1:
输入:[3,9,20,null,null,15,7]
输出:[[9],[3,15],[20],[7]]
解释:
在不丧失其普遍性的情况下,我们可以假设根结点位于 (0, 0):
然后,值为 9 的结点出现在 (-1, -1);
值为 3 和 15 的两个结点分别出现在 (0, 0) 和 (0, -2);
值为 20 的结点出现在 (1, -1);
值为 7 的结点出现在 (2, -2)。
示例 2:
输入:[1,2,3,4,5,6,7]
输出:[[4],[2],[1,5,6],[3],[7]]
解释:
根据给定的方案,值为 5 和 6 的两个结点出现在同一位置。
然而,在报告 "[1,5,6]" 中,结点值 5 排在前面,因为 5 小于 6。
提示:
- 树的结点数介于
1
和1000
之间。 - 每个结点值介于
0
和1000
之间。
2. 思路分析:
① 从题目中可以知道我们需要求解的是从左到右的横坐标相同的点的集合,并且还需要注意假如横坐标相同那么结果应该从上到下点的集合,所以我们需要做的是通过递归遍历二叉树,计算出每一个节点对应的坐标,将相同横坐标的点放在一起,计算每一个节点的位置其实这个问题很简单,只需要在方法中传递两个int类型的x,y参数,往左边递归的时候x减1,这里需要注意的是y的变化,从题目中可以知道当前根节点的左右子树的真实坐标分别是(x - 1, y -1),(x + 1, y - 1)也就是往下的时候坐标y值是在减小的,但是我们在递归的时候需要y值应该是y + 1的,也就是增加的,其实y - 1与y + 1表示的是点y的坐标的一个趋势,只是往上还是往下的问题(主要是保持左右子树y值的趋势是一致的那么节点的坐标就是正确的),而题目中有一个问题是如果横坐标是一样的话我们需要从上往下输出对应的节点,所以我们需要通过y + 1递增来解决这个问题,这样递归之后y值就是不断增大的,这样我们在对节点进行排序之后y值较小的就会排列在前面也就会先访问,这样得到的结果才是正确的
② 因为涉及到两个坐标以及坐标对应的值,所以可以使用python中一个很方便的技巧就是使用字典进行映射,这样就可以表示z = f(x, y)三个值之间的关系,这样就可以将对应的横坐标与纵坐标的对应位置放在一起了,最后对字典进行排序,这样可以根据字典中的值添加到结果集中
3. 代码如下:
import collections
import sys
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, x: int, y: int, rec: collections.defaultdict(list)):
if not root: return
# 注意有可能多个坐标都是一样的所以需要使用collections.defaultdict(list)使一个键映射多个值
rec[(x, y)].append(root.val)
self.dfs(root.left, x - 1, y + 1, rec)
self.dfs(root.right, x + 1, y + 1, rec)
def verticalTraversal(self, root: TreeNode) -> List[List[int]]:
rec = collections.defaultdict(list)
self.dfs(root, 0, 0, rec)
flag, cur, res = -sys.maxsize, list(), list()
# 对x, y坐标进行排序
for key, value in sorted(rec.keys()):
if key != flag:
res.append(cur)
flag = key
cur = list()
# 将相同x,y坐标的排序添加到临时的列表中
cur.extend(sorted(t for t in rec[(key, value)]))
res.pop(0)
res.append(cur)
return res