104. 二叉树的最大深度
- 给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回它的最大深度 3
思路1:递归
递归通式:
判断一个节点的最大深度,需要取左子树或右子树最大的值加上当前节点,即max(root.left, root.right) + 1。
退出条件:
当左或右子树为空时,返回深度0。
代码实现1:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
else:
left_depth = self.maxDepth(root.left)
right_depth = self.maxDepth(root.right)
return max(left_depth, right_depth) + 1
思路2:迭代
用一个元组存放当前深度和当前节点,并将其入栈[(1, root)]
取栈顶元素(1, root),1为当前深度,root为当前节点,更新最大深度
然后看其有无左右子树,若有,更新当前深度并和当前节点一起组成元素,再入栈[(2, item1), (2, item2)]
代码实现2:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
queue = [(1, root)]
depth = 0
while queue:
# cur_depth, item = queue.pop(0) 此处使用栈,pop尾部更快
cur_depth, item = queue.pop()
if item:
depth = max(depth, cur_depth)
queue.append((cur_depth + 1, item.left))
queue.append((cur_depth + 1, item.right))
return depth
110. 平衡二叉树
- 给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例:
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
思路1:由上至下
对于根节点,判断左子树和右子树的深度差是否小于2,若是,则判断根节点左子树……直到所有节点判断完。
代码实现1:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def depth(self, item):
if item is None:
return 0
left_depth = self.depth(item.left)
right_depth = self.depth(item.right)
return max(left_depth, right_depth) + 1
def isBalanced(self, root: TreeNode) -> bool:
if root is None:
return True
return abs(self.depth(root.left) - self.depth(root.right)) < 2 and self.isBalanced(root.left) and self.isBalanced(root.right)
思路2:由下至上
递归通式:
当前节点的左右子树深度差,当前节点深度
递归出口:
如果当前节点为空、左子树不平衡、右子树不平衡,退出
代码实现2:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def balance_helper(self, item):
if item is None:
return True, -1
left_balance, left_height = self.balance_helper(item.left)
if not left_balance:
return False, 0
right_balance, right_height = self.balance_helper(item.right)
if not right_balance:
return False, 0
return (abs(left_height - right_height) < 2), 1 + max(left_height, right_height)
def isBalanced(self, root: TreeNode) -> bool:
if root is None:
return True
return self.balance_helper(root)[0]
543. 二叉树的直径
- 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例:
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。
思路1:子问题定义
二叉树的直径(即最长路径),不一定经过根结点。
二叉树的解题技巧是,首先判断问题 能否划分为子问题、应当划分为什么样的子问题。二叉树直径实际上就是二叉树中的最长路径,我们是可以划分出子问题的:
- 二叉树的最长路径 = max { 左 子 树 的 最 长 路 径 , 右 子 树 的 最 长 路 径 , 经 过 根 结 点 的 最 长 路 径 } \max \{ 左子树的最长路径, 右子树的最长路径, 经过根结点的最长路径 \} max{左子树的最长路径,右子树的最长路径,经过根结点的最长路径}
其中左子树的最长路径和右子树的最长路径是两个可以递归求解的子问题,那么经过根结点的最长路径如何计算呢?是左子树的深度加上右子树的深度。代入上面的式子得到:
- 二叉树的最长路径 = max { 左 子 树 的 最 长 路 径 , 右 子 树 的 最 长 路 径 , 左 子 树 的 深 度 + 右 子 树 的 深 度 } \max \{ 左子树的最长路径, 右子树的最长路径, 左子树的深度 + 右子树的深度 \} max{左子树的最长路径,右子树的最长路径,左子树的深度+右子树的深度}
这里出现了两个子问题:子树的最大直径、子树的最大深度。这难道是要把树遍历两遍吗?非也,我们只需要让遍历函数返回两个值即可。
Python 通过 tuple 类型很好地支持了函数的多返回值。
代码实现1:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二元 tuple (depth, diameter)
# depth 表示子树的最大深度,diameter 表示子树的最长路径(直径)
def traverse(root):
if root is None:
return (0, 0)
left_depth, left_diam = traverse(root.left)
right_depth, right_diam = traverse(root.right)
# 求二叉树深度的常规方法
depth = 1 + max(left_depth, right_depth)
# 套用上面推导出的最长路径公式
diam = max(left_diam, right_diam, left_depth + right_depth)
return depth, diam
def diameterOfBinaryTree(root):
return traverse(root)[1]
思路2:全局变量
如果我们看函数返回的第二个值,也就是子树的直径(最长路径),我们会发现,我们所做的,不过是递归计算左右子树的最长路径,然后再通过这个计算出当前树的最长路径。既然我们始终都是在求它的最大值,那么用一个全局变量保存它的最大值不就可以了?也就是说,我们把最大直径放在函数返回值里,是让其中的某个最大值一层一层地返回上去,直到 DFS 的起点。而使用全局变量的话,则是让最大值可以直接抵达终点。这样,我们的函数就可以只返回一个变量了。
代码实现2:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.maxd = 0
def depth(self, item):
if item is None:
return 0
left = self.depth(item.left)
right = self.depth(item.right)
self.maxd = max(left+right, self.maxd)
return max(left, right) + 1
def diameterOfBinaryTree(self, root: TreeNode) -> int:
if root is None:
return 0
self.depth(root)
return self.maxd