Concepts

image.png
Full: 每个node只有0个或2个children
complete: 从上到下,从左到右
perfect: 每一层都是满的 2**k-1

Focus on a Node! 不要去想递归的过程,不要去想整棵树,就想这一个node要做什么!
What needs to happen at this node?

中序遍历中的一些点

  1. successor, 后驱节点,紧跟着当前节点比当前小的那个节点,先取root.right(如果root.right存在),然后一直取左子树

    1. def succeesor(root):
    2. root = root.right
    3. while root.left:
    4. root = root.left
    5. return root
  2. prece, 前驱,比当前节点大一个位置的节点

    1. def predecessor(root):
    2. root = root.left
    3. while root.right:
    4. root = root.right
    5. return root

    94. 二叉树的中序遍历

    https://leetcode-cn.com/problems/binary-tree-inorder-traversal/submissions/

    1. # Definition for a binary tree node.
    2. # class TreeNode:
    3. # def __init__(self, val=0, left=None, right=None):
    4. # self.val = val
    5. # self.left = left
    6. # self.right = right
    7. class Solution:
    8. def inorderTraversal(self, root: TreeNode) -> List[int]:
    9. stack = list()
    10. arr = list()
    11. while root or stack:
    12. while root:
    13. stack.append(root)
    14. root = root.left
    15. root = stack.pop()
    16. arr.append(root.val)
    17. root = root.right
    18. return arr

    230.二叉搜索树中第K小的元素

    https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/

    1. class Solution:
    2. def kthSmallest(self, root: TreeNode, k: int) -> int:
    3. stack = list()
    4. while root or stack:
    5. while root:
    6. stack.append(root)
    7. root = root.left
    8. root = stack.pop()
    9. k -= 1
    10. if not k:
    11. return root.val
    12. else:
    13. root = root.right

同理找第K大的

  1. class Solution:
  2. def kthLargest(self, root: TreeNode, k: int) -> int:
  3. stack = list()
  4. while root or stack:
  5. while root:
  6. stack.append(root)
  7. root = root.right
  8. root = stack.pop()
  9. k-=1
  10. if k==0:
  11. return root.val
  12. root = root.left
  13. return

98. 验证二叉搜索树

https://leetcode-cn.com/problems/validate-binary-search-tree/
Iteration

  1. class Solution:
  2. def isValidBST(self, root: TreeNode) -> bool:
  3. if not root:
  4. return False
  5. stack = list()
  6. pre = None
  7. while root or stack:
  8. while root:
  9. stack.append(root)
  10. root = root.left
  11. root = stack.pop()
  12. if pre and root.val<=pre.val:
  13. return False
  14. pre = root
  15. root = root.right
  16. return True

Recursion
在纸上画一画,理解一下这个preNode的含义,出现的位置,preNode是如何保证整个左子树小于root,整个右子树都大于root的。

  1. class Solution:
  2. pre = None
  3. def isValidBST(self, root: TreeNode) -> bool:
  4. if not root:
  5. return True
  6. left = self.isValidBST(root.left)
  7. if self.pre and root.val<=self.pre.val:
  8. return False
  9. self.pre = root
  10. right = self.isValidBST(root.right)
  11. return left and right

04. 二叉树的最大深度

https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/

  1. class Solution:
  2. def maxDepth(self, root: TreeNode) -> int:
  3. if not root:
  4. return 0
  5. return 1+max(self.maxDepth(root.left),self.maxDepth(root.right))

110. 平衡二叉树

https://leetcode-cn.com/problems/balanced-binary-tree/
自顶向下
O(n), O(n)

  1. class Solution:
  2. def isBalanced(self, root: TreeNode) -> bool:
  3. def recur(root):
  4. if not root:
  5. return 0
  6. left = recur(root.left)
  7. if left == -1:
  8. return -1
  9. right = recur(root.right)
  10. if right == -1:
  11. return -1
  12. return max(left,right) + 1 if abs(left-right)<2 else -1
  13. return recur(root)!=-1

自底向上
O(NlogN), O(N)

  1. class Solution:
  2. def maxDepth(self,root):
  3. if not root:
  4. return 0
  5. return 1+max(self.maxDepth(root.left),self.maxDepth(root.right))
  6. def isBalanced(self, root: TreeNode) -> bool:
  7. if not root:
  8. return True
  9. return abs(self.maxDepth(root.left)-self.maxDepth(root.right))<2 and self.isBalanced(root.left) and self.isBalanced(root.right) # 先序遍历,判断当前子树是否二叉平衡树, 判断当前左子树是否二叉平衡树, 判断当前右子树是否二叉平衡树

538. 把二叉搜索树转换为累加树

https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
因为每个节点要之后它右边所有子树之和,所以这道题是inorder的反向操作
访问每一个节点时,我们希望维护一个变量 sum,保存「比当前节点值大的所有节点值的和」。
二叉搜索树的中序遍历,访问的节点值是递增的。
如果先访问右子树,反向的中序遍历,访问的节点值是递减的,之前访问的节点值都比当前的大,每次累加给 sum 即可
image.png
image.png

  1. class Solution:
  2. total = 0
  3. def convertBST(self, root: TreeNode) -> TreeNode:
  4. if root:
  5. self.convertBST(root.right)
  6. self.total += root.val
  7. root.val = self.total
  8. self.convertBST(root.left)
  9. return root

654. 最大二叉树

https://leetcode-cn.com/problems/maximum-binary-tree/
第一次秒做出来二叉树的medium题目

  1. class Solution:
  2. def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
  3. if not nums:
  4. return
  5. max_val = max(nums)
  6. max_idx = nums.index(max_val)
  7. root = TreeNode(max_val)
  8. root.left = self.constructMaximumBinaryTree(nums[:max_idx])
  9. root.right = self.constructMaximumBinaryTree(nums[max_idx+1:])
  10. return root

543. 二叉树的直径

https://leetcode-cn.com/problems/diameter-of-binary-tree/
这道题更新diameter的方式和最后的返回值是一个关键点,返回给节点的是这个节点的最大深度,而不是diameter
每次在root计算左右子树的深度加起来有多长,这个是diameter,但是返回的时候是返回这个节点的最大深度

  1. class Solution:
  2. diameter = 0
  3. def diameterOfBinaryTree(self, root: TreeNode) -> int:
  4. def helper(root,diameter):
  5. if not root:
  6. return 0
  7. left = helper(root.left,diameter)
  8. right = helper(root.right,diameter)
  9. self.diameter = max(left+right,self.diameter)
  10. return max(left,right)+1
  11. helper(root,self.diameter)
  12. return self.diameter

112. 路径总和

https://leetcode-cn.com/problems/path-sum/

  1. class Solution:
  2. def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
  3. if not root:
  4. # 注意这个返回值,如果可以一直到none节点,那说明这条路径上没找到,所以返回false
  5. return False
  6. if root.val == targetSum and not root.left and not root.right:
  7. return True
  8. return self.hasPathSum(root.left, targetSum-root.val) or self.hasPathSum(root.right, targetSum-root.val)

437. 路径总和 III

这道题还有1 和 2
https://leetcode-cn.com/problems/path-sum-iii/
先序遍历,用一个list把这条路径上所有可能的加和都存下来,相对好理解一些,但是时间复杂度比较高,每一层都要对sumlist进行两次遍历, 第一次对每个元素加一,第二次算count

  1. class Solution:
  2. def pathSum(self, root: TreeNode, sum: int) -> int:
  3. def dfs(root, sumlist):
  4. if root is None:
  5. return 0
  6. sumlist = [num+root.val for num in sumlist]
  7. sumlist.append(root.val)
  8. count = sumlist.count(sum)
  9. return count+dfs(root.left, sumlist)+dfs(root.right, sumlist)
  10. return dfs(root, [])

前缀和,晚上状态不太好,理解不了这个了,迷迷糊糊的
当前节点的前缀和减去target如果存在在hashmap中,说明这条路径之前有个节点的前缀和是presum-target
那当前节点减去那个节点就是 presum - (presum-target) = target
这个思路有点像经典的twoSum
用一个HashMap来存前缀和记录,key是前缀和,value是这个前缀和出现过几次,注意关键是在这条路径上出现过几次,因为从一个节点回到root只有一条路径,这个路径上同一个前缀和可能多次出现。
所以关键的那一步 返回本层,就是把当前前缀和出现的次数减一,因为我们退回去了。

  1. def pathSum(root,sum):
  2. #思路:前缀和
  3. #递归进入左右子树后,回到当前层,要把当前节点添加的前缀和去除,避免回溯之后影响上一层。
  4. def dfs(root, hashmap, target,presum):
  5. #终止条件
  6. if not root:
  7. return 0
  8. #本层要做的事情
  9. cnt=0
  10. presum += root.val
  11. if presum - target in hashmap:
  12. cnt += hashmap[presum - target]
  13. if presum not in hashmap:
  14. hashmap[presum] = 1
  15. else:
  16. hashmap[presum]+=1
  17. #进入下一层
  18. cnt+=dfs(root.left, hashmap, target,presum)
  19. cnt+=dfs(root.right, hashmap, target,presum)
  20. #!!!重要:回到本层
  21. hashmap[presum]-=1
  22. return cnt
  23. hashmap = {0:1}
  24. r = dfs(root, hashmap,sum,0)
  25. return r

101. 对称二叉树

https://leetcode-cn.com/problems/symmetric-tree/
或者叫做镜像二叉树
这道题的关键在于递归函数返回值和输入值的设计!

  1. class Solution:
  2. def isSymmetric(self, root: TreeNode) -> bool:
  3. def helper(left, right):
  4. if not left and not right:
  5. return True
  6. if not left or not right:
  7. return False
  8. # left is left's left or left's right
  9. # right is right's right or right's left
  10. if left.val !=right.val:
  11. return False
  12. return helper(left.left, right.right) and helper(left.right,right.left)
  13. if not root:
  14. return True
  15. else:
  16. return helper(root.left, root.right)

BFS, 层序遍历

  1. from collections import deque
  2. class Solution:
  3. def isSymmetric(self, root: TreeNode) -> bool:
  4. if not root:
  5. return True
  6. queue = deque()
  7. queue.append(root)
  8. while queue:
  9. level_list=[]
  10. for _ in range(len(queue)):
  11. tmp = queue.popleft() # 注意这里是先进先出,所以是popleft()
  12. if not tmp:
  13. level_list.append(-1)
  14. else:
  15. level_list.append(tmp.val)
  16. # 下面这两句要放在else的逻辑下,因为如果是一个node是None,那么它的左右节点不能继续
  17. #往list里放,否则程序无法终止
  18. queue.append(tmp.left)
  19. queue.append(tmp.right)
  20. if level_list[:] == level_list[::-1]:
  21. continue
  22. else:
  23. return False
  24. return True

再来一道很类似的层序遍历

剑指 Offer 32 - I. 从上到下打印二叉树

https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/
上一题如果children是none也要继续往queue里放是因为我们要把空children用-1来补位置,来确保对称检查;但是如果节点本身是none,则不能继续放入queue,这样会死循环

  1. from collections import deque
  2. class Solution:
  3. def levelOrder(self, root: TreeNode) -> List[int]:
  4. queue = deque()
  5. if not root:
  6. return []
  7. res = []
  8. queue.append(root)
  9. while queue:
  10. for _ in range(len(queue)):
  11. tmp = queue.popleft()
  12. res.append(tmp.val)
  13. if tmp.left:
  14. queue.append(tmp.left)
  15. if tmp.right:
  16. queue.append(tmp.right)
  17. return res

236. 二叉树的最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

  1. class Solution:
  2. def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
  3. if not root or root==p or root==q:
  4. return root
  5. left = self.lowestCommonAncestor(root.left, p, q)
  6. right = self.lowestCommonAncestor(root.right, p, q)
  7. if not left:
  8. return right
  9. if not right:
  10. return left
  11. return root

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/
注意这道题和上一题的区别 这道题是二叉搜索树,可以利用二叉搜索树的特性,让题目变的更简单

  1. # 迭代
  2. class Solution:
  3. def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
  4. # 确保p <= q
  5. if p.val > q.val:
  6. p,q=q,p
  7. while root:
  8. if root.val < p.val:
  9. root=root.right
  10. elif root.val > q.val:
  11. root=root.left
  12. else:
  13. break
  14. return root
  15. # 递归
  16. class Solution:
  17. def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
  18. if not root:
  19. return
  20. if root.val > q.val and root.val > p.val:
  21. return self.lowestCommonAncestor(root.left,p,q)
  22. if root.val<q.val and root.val < p.val:
  23. return self.lowestCommonAncestor(root.right, p, q)
  24. return root

二叉树序列化和反序列化

  1. from collections import deque
  2. class Codec:
  3. def serialize(self, root):
  4. if not root:
  5. return "[]"
  6. res = []
  7. queue = deque()
  8. queue.append(root)
  9. while queue:
  10. node = queue.popleft()
  11. if node:
  12. res.append(str(root.val))
  13. queue.append(root.left)
  14. queue.append(root.right)
  15. else:
  16. res.append('null')
  17. return '[' + ','.join(res) + ']'
  18. def deserialize(self, data):
  19. if data=='[]':return
  20. dataList = data[1:-1].split(",")
  21. root = TreeNode(int(dataList[0]))
  22. queue = deque()
  23. queue.append(root)
  24. i = 1
  25. while queue:
  26. node = queue.popleft()
  27. if dataList[i]!='null':
  28. node.left = TreeNode(int(dataList[i]))
  29. queue.append(node.left)
  30. i+=1
  31. if dataList[i]!='null':
  32. node.right = TreeNode(int(dataList[i]))
  33. queue.append(node.right)
  34. i+=1
  35. return root

剑指 Offer 26. 树的子结构

https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/
树的匹配类问题,两步:

  1. 找到A里和B的根节点相等的点C
  2. 从C开始匹配
  3. 两个递归函数
  4. 主函数是用来找那个相等的C点,先进去check,如果不相等会进到or的下一个条件left,去left找,找不到去right找,如果找到了就会在check里递归

    1. class Solution:
    2. def check(self, t, s):
    3. '''
    4. if s is a subtree of t
    5. '''
    6. if not s: return True
    7. if not t: return False
    8. return t.val==s.val and self.check(t.left,s.left) and self.check(t.right, s.right)
    9. def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
    10. if not A or not B: return False
    11. return self.check(A,B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B)

    572. 另一个树的子树

    https://leetcode-cn.com/problems/subtree-of-another-tree/
    注意这一题和上一题判断条件的不同之处
    这道题必须同时遍历结束才算匹配

    1. class Solution:
    2. def check(self,s,t):
    3. if not s and not t:
    4. return True
    5. if not s or not t:
    6. return False
    7. if s.val!=t.val:
    8. return False
    9. return s.val==t.val and self.check(s.left,t.left) and self.check(s.right,t.right)
    10. def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
    11. if not s or not t:
    12. return False
    13. return self.check(s,t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)

    103. 二叉树的锯齿形层序遍历

    https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
    注意改变打印方向的时候,改的是往result里放的顺序,是尾插入还是头插入,而不是改变放队列里放的顺序

    1. from collections import deque
    2. class Solution:
    3. def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
    4. queue = deque()
    5. if not root:
    6. return []
    7. res = []
    8. queue.append(root)
    9. flag = True
    10. while queue:
    11. level_list = deque()
    12. flag = not flag
    13. for _ in range(len(queue)):
    14. tmp = queue.popleft()
    15. if flag:
    16. level_list.appendleft(tmp.val)
    17. else:
    18. level_list.append(tmp.val)
    19. if tmp.left:
    20. queue.append(tmp.left)
    21. if tmp.right:
    22. queue.append(tmp.right)
    23. res.append(list(level_list))
    24. return res

    116. 填充每个节点的下一个右侧节点指针

    https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
    层序遍历

    1. class Solution:
    2. def connect(self, root: 'Node') -> 'Node':
    3. res = root
    4. while root and root.left:
    5. # 保留下一层最左边节点
    6. next = root.left
    7. # 遍历当前层,但是在给下一层节点的next赋值
    8. # 每次当前层横向遍历,从左到右
    9. while root:
    10. root.left.next = root.right
    11. # 下面这句等价于:
    12. # if root.next:
    13. # root.right.next = root.next.left
    14. # else:
    15. # root.right.next = None
    16. # OR
    17. # root.right.next = root.next.left if root.next else None
    18. root.right.next = root.next and root.next.left
    19. root = root.next
    20. root = next #去到下一层最左边
    21. return res

    递归 dfs

    1. class Solution:
    2. def connect(self, root: 'Node') -> 'Node':
    3. def dfs(root):
    4. if not root.left and not root.right:
    5. return
    6. root.left.next = root.right
    7. root.right.next = root.next.left if root.next else None
    8. dfs(root.left)
    9. dfs(root.right)
    10. return root
    11. if not root:
    12. return root
    13. elif not root.left:
    14. root.next = None
    15. return root
    16. else:
    17. return dfs(root)

    拉拉链的递归

    1. class Solution:
    2. def connect(self, root: 'Node') -> 'Node':
    3. def dfs(root):
    4. if not root:
    5. return
    6. left = root.left
    7. right = root.right
    8. while left:
    9. left.next = right
    10. left = left.right
    11. right = right.left
    12. dfs(root.left)
    13. dfs(root.right)
    14. return root
    15. return dfs(root)

    117. 填充每个节点的下一个右侧节点指针 II

    https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
    上一题的拓展,此时不是完全二叉树,那么下一层的第一个节点就不是每层开始的节点的left
    需要更巧妙的办法来保存下一层的起始点

    1. class Solution:
    2. def connect(self, root: 'Node') -> 'Node':
    3. first = root
    4. while first:
    5. head = tail = Node()
    6. cur = first
    7. while cur:
    8. if cur.left:
    9. tail.next = cur.left
    10. tail = tail.next
    11. if cur.right:
    12. tail.next = cur.right
    13. tail = tail.next
    14. cur = cur.next
    15. first = head.next
    16. return root

    上面的写法看着简洁,但是理解起来不是很直观, head=tail=Node() 这种赋值方式,第一次tail.next更新时,head是跟着一起更新的,之后tail再更新,就和head的索引不一样了

    1. def connect(self, root: 'Node') -> 'Node':
    2. first = root
    3. while first:
    4. head = None
    5. tail = Node()
    6. cur = first
    7. while cur:
    8. if cur.left:
    9. if not head:
    10. head = cur.left
    11. tail.next = cur.left
    12. tail = tail.next
    13. if cur.right:
    14. if not head:
    15. head = cur.right
    16. tail.next = cur.right
    17. tail = tail.next
    18. cur = cur.next
    19. first = head
    20. return root

    297. 二叉树的序列化与反序列化

    https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/
    递归,也就是dfs的序列化是按照 root,left,right三大块的顺序存储的
    而迭代,也就是bfs的序列化是按照一层一层的顺序存储的
    image.png
    递归写法

    1. from collections import deque
    2. class Codec:
    3. def serialize(self, root):
    4. """Encodes a tree to a single string.
    5. :type root: TreeNode
    6. :rtype: str
    7. """
    8. if not root:
    9. return 'X,'
    10. leftStr = self.serialize(root.left)
    11. rightStr = self.serialize(root.right)
    12. return str(root.val) + ',' + leftStr + rightStr
    13. def deserialize(self, data):
    14. """Decodes your encoded data to tree.
    15. :type data: str
    16. :rtype: TreeNode
    17. """
    18. queue = deque(data.split(","))
    19. return self.helper(queue)
    20. def helper(self,queue):
    21. cur_node = queue.popleft()
    22. if cur_node == 'X':
    23. return None
    24. newNode = TreeNode(cur_node)
    25. newNode.left = self.helper(queue)
    26. newNode.right = self.helper(queue)
    27. return newNode

    迭代
    deserialize的时候,如果弹出来的是X,那么就不用管这个child了,初始化时就是none

    1. from collections import deque
    2. class Codec:
    3. def serialize(self, root):
    4. """Encodes a tree to a single string.
    5. :type root: TreeNode
    6. :rtype: str
    7. """
    8. if not root:
    9. return ''
    10. queue = deque()
    11. queue.append(root)
    12. res = ''
    13. while queue:
    14. for _ in range(len(queue)):
    15. node = queue.popleft()
    16. if not node:
    17. res +='X,'
    18. else:
    19. res +=str(node.val) + ','
    20. queue.append(node.left)
    21. queue.append(node.right)
    22. return res
    23. def deserialize(self, data):
    24. """Decodes your encoded data to tree.
    25. :type data: str
    26. :rtype: TreeNode
    27. """
    28. if not data:
    29. return
    30. data = deque(data.split(","))
    31. root = TreeNode(data.popleft())
    32. queue = deque()
    33. queue.append(root)
    34. while queue:
    35. for _ in range(len(queue)):
    36. node = queue.popleft()
    37. if data:
    38. val = data.popleft()
    39. if val!='X':
    40. node.left = TreeNode(val)
    41. queue.append(node.left)
    42. if data:
    43. val = data.popleft()
    44. if val!='X':
    45. node.right = TreeNode(val)
    46. queue.append(node.right)
    47. return root

    450. 删除二叉搜索树中的节点

    https://leetcode-cn.com/problems/delete-node-in-a-bst/
    这道题一开始困惑的地方在于,如果是链表的删除,那么是用这个点之前的点指向这个点next的点。用这样的思路解决这道题会发现找不到当前节点的父节点,所以不能按照链表的思路去修改指针,而要去修改的是节点的值,让值修改完之后保持BST的特性就可以了,那么关键就在于这个值用哪个值,根据节点不同的情况,选择直接删除或者前驱或者后继

  5. 根据当前root.val和key的大小判断决定取左边删还是右边删,记得递归函数返回的是删除掉目标节点后的子树,所以最后是返回了root,所以取哪边找要重新复制给那个方向left or right

  6. 如果是叶子节点最简单,直接删除,注意删除操作是置为None
  7. 如果right存在,说明是有后继的,不管left有没有,因为即便left有,也要把right那边的最小值拿过来。所以如果right存在,就去right找right子树的最小值,也就是当前子树的后继, 从当前子树的right一直找左子树找到最后一个
  8. 如果没有right,有left,那么就去拿当前节点的前驱,用前驱的值覆盖,然后删除前驱

    1. class Solution:
    2. def successor(self, root):
    3. root = root.right
    4. while root.left:
    5. root = root.left
    6. return root.val
    7. def predecessor(self, root):
    8. root = root.left
    9. while root.right:
    10. root = root.right
    11. return root.val
    12. def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
    13. if not root:
    14. return
    15. if root.val > key:
    16. # search in left subtree
    17. root.left = self.deleteNode(root.left, key)
    18. elif root.val < key:
    19. root.right = self.deleteNode(root.right, key)
    20. else:
    21. #如果是叶子节点,直接删除
    22. if not root.left and not root.right:
    23. root = None
    24. #如果有右子树
    25. elif root.right:
    26. root.val = self.successor(root)
    27. root.right = self.deleteNode(root.right, root.val)
    28. else:
    29. root.val = self.predecessor(root)
    30. root.left = self.deleteNode(root.left, root.val)
    31. return root

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

    https://leetcode-cn.com/problems/all-nodes-distance-k-in-binary-tree/
    可以添加父节点指针的例子