- LeetCode Hot 100 是最常被考察的题目集合,涵盖了面试中常见的算法和数据结构问题。
- 刷 Hot 100可以让你在有限的时间内集中精力解决最常考的问题。
- 不仅要写出代码,还要理解问题的本质、优化解法和复杂度分析。
- 遇到问题要多交流多求问多分享,“多折腾”能加深印象。
Day 4 二叉树专题训练
二叉树的题目,套路就几种,都刷一遍就有感觉了。递归,前序、中序、后序、层序遍历,以及在此基础上的变形。
在更新过程中发现前面做过的题会忘,所以可以4天一个周期。第4天做前面3天的题,补充笔记。
思路:
① 递归,深度优先遍历,左中右。
② 通过栈模拟递归的过程。
时间复杂度O(n),空间复杂度O(h).
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
self.res = []
self.dfs(root)
return self.res
def dfs(self, node):
if not node: return
self.dfs(node.left) # 递归,中序遍历,左 -> 中 -> 右
self.res.append(node.val)
self.dfs(node.right)
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return [] # 二叉树为空,直接返回空列表
stack = [] # 用于存储待处理的节点
res = [] # 用于存储遍历结果
cur = root # 当前节点,初始化为根节点 root
while cur or stack: # 当前节点或者栈不为空,继续遍历
if cur:
stack.append(cur) # 不为空,则将该节点压入栈中
cur = cur.left # 移动到其左子节点
else:
cur = stack.pop() # 弹出栈顶元素,加入结果列表
res.append(cur.val)
cur = cur.right
return res
思路: 使用一个队列,每次popleft遍历该层所有元素,有子节点则加入队列。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
queue = collections.deque([root]) # 队列先进先出,用于BFS
res = []
while queue: # 队列有值,则继续
temp = []
for i in range(len(queue)): # 每层所有节点遍历,加入左右子节点
cur = queue.popleft()
temp.append(cur.val)
if cur.left: queue.append(cur.left)
if cur.right: queue.append(cur.right)
res.append(temp)
return res
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
思路:
① 递归,终止条件为到达叶子节点,则其深度为0. 递归遍历左子树和右子树,取最大值 + 1(节点本身)。
② 层序遍历,每遍历一层,给depth + 1. 时间复杂度O(n),空间复杂度O(m),取决于队列长度。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0 # 终止条件,到达空节点,深度为0
left = self.maxDepth(root.left) # 递归遍历左子树,右子树
right = self.maxDepth(root.right)
return max(left, right)+1 # 子树最大深度加节点本身1
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
depth = 0
queue = collections.deque([root])
while queue: # 层序遍历
depth += 1
for i in range(len(queue)):
cur = queue.popleft() # 加入左右节点
if cur.left: queue.append(cur.left)
if cur.right: queue.append(cur.right)
return depth
思路:
① 递归。时间复杂度O(n),空间复杂度O(n).
② 层序遍历,对于每个节点都交换其左右节点。时间复杂度O(n),空间复杂度O(m).
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root: return None
left = self.invertTree(root.left) # 翻转左子树
right = self.invertTree(root.right) # 翻转右子树
root.left, root.right = root.right, root.left # 交换左右节点
return root
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root: return None
queue = collections.deque([root])
while queue: # 队列不为空,继续
for i in range(len(queue)):
cur = queue.popleft() # 对于本层中的每个元素,翻转左右子树
cur.left, cur.right = cur.right, cur.left
if cur.left: queue.append(cur.left)
if cur.right: queue.append(cur.right)
return root
思路: 左右子节点存在且相等,且两子节点对称(左子节点的左半部分与右子节点的右半部分相等 and so on)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
def isSame(left, right): # 对称:两子树存在,且值相等,左右子树对称
if not left and not right: return True
if left and not right: return False
if not left and right: return False
if left.val != right.val: return False
outside = isSame(left.left, right.right)
inside = isSame(right.left, left.right)
return outside and inside
return isSame(root.left, root.right)
思路: 借助最大深度思路,直径可以递归求深度的同时,更新为左子树和右子树深度和。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
self.ans = 0 # 初始化 ans
self.depth(root)
return self.ans - 1 # 直径是路径长度,路径长度 = 节点数 - 1
def depth(self, root):
if not root: return 0 # 空节点深度为 0
left = self.depth(root.left) # 左子树深度
right = self.depth(root.right) # 右子树深度
self.ans = max(self.ans, left + right + 1) # 更新最大直径
return max(left, right) + 1 # 返回当前子树的深度