二叉树的中序遍历
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 中序:左中右
ans, stk = [], [] # 用栈来存中间结果 先进后出
while root or stk:
if root:
stk.append(root) # 根节点先入栈
root = root.left # 不断访问左子树、直到最左的叶子节点/中节点
else:
root = stk.pop() # 先出最左边/中节点,先进后出
ans.append(root.val) # 保存结果
root = root.right # 出一个就可以取它的右节点了、因为左节点和根节点(左节点出的时候其没有右节点、所以root回到根节点)已经出栈了
return ans
二叉树的最大深度
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
queue, res = [root], 0
while queue:
tmp = []
for node in queue: # 用层次遍历的方式得到最大深度
if node.left:
tmp.append(node.left) # tmp存的是下一层的节点
if node.right:
tmp.append(node.right)
queue = tmp
res += 1 # 每一层访问完深度+1
return res
翻转二叉树
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root: return root
self.invertTree(root.left)
self.invertTree(root.right)
root.left, root.right = root.right, root.left # 后序遍历(左右中)的方式迭代
return root
对称二叉树
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
def recur(L, R):
if not L and not R: return True
if not L or not R or L.val != R.val: return False
return recur(L.left, R.right) and recur(L.right, R.left) # 先序遍历(中左右)的方式迭代
return not root or recur(root.left, root.right)
二叉树的直径
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度(父子节点之间的长度是1)
class Solution:
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
ans = 0
def dfs(node):
if node is None:
return -1
l_len = dfs(node.left) + 1 # 如果有左节点、左子树长度+1
r_len = dfs(node.right) + 1
nonlocal ans
ans = max(ans, l_len + r_len) # 只记录最大值
return max(l_len, r_len) # 返回以该节点为起点的最大值 后序遍历
dfs(root)
return ans
二叉树的层序遍历
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
q = deque([root]) # 记录每一层的节点
res = []
while q:
tmp = []
n = len(q)
for i in range(n): # 遍历当前层的节点
i = q.popleft() # 队列、先进先出
if i.left:
q.append(i.left) # 下一层节点依次 入队列
if i.right:
q.append(i.right)
tmp.append(i.val) # 记录当前层的单个值
res.append(tmp) # 记录当前层的所有值
return res
将有序数组转换为二叉搜索树
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
return self.build(nums, 0, len(nums) - 1)
def build(self, nums, l, r):
if l > r:
return None
mid = (l + r) // 2
ans = TreeNode(nums[mid]) # 先序遍历、先固定头节点
ans.left = self.build(nums, l, mid - 1) # 左子树
ans.right = self.build(nums, mid + 1, r) # 右子树
return ans
验证二叉搜索树
class Solution:
def isValidBST(self, root: Optional[TreeNode], left=float('-inf'), right=float('inf')) -> bool:
if root is None:
return True # 空节点始终是合法的BST
x = root.val # 当前节点的值
return left < x < right and \ # 当前节点的值必须在(left, right)范围内
self.isValidBST(root.left, left, x) and \ # 递归检查左子树,更新上界为当前节点值
self.isValidBST(root.right, x, right) # 递归检查右子树,更新下界为当前节点值
# 第二步比较的是 left节点是否小于父节点值
# 第三步比较的是 right节点是否大于父节点值
二叉搜索树中第k小的值
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
# 使用中序遍历找到第 k 小的元素
def dfs(root):
if not root: # 如果当前节点为空,则返回(递归终止条件)
return
dfs(root.left) # 递归访问左子树(中序遍历的左部分)
if self.k == 0: # 如果已经找到了第 k 小的元素,提前退出递归
return
self.k -= 1 # 处理当前节点,减小 k 的值
if self.k == 0: # 如果 k 减为 0,说明当前节点是第 k 小的元素
self.res = root.val # 记录结果
dfs(root.right) # 递归访问右子树(中序遍历的右部分)
self.k = k # 初始化 k
dfs(root) # 开始中序遍历
return self.res # 返回找到的第 k 小的元素
二叉树的右视图
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
# 层序遍历只要最后一个值
if not root:
return []
q = deque([root])
res = []
while q:
l = len(q)
for i in range(l):
n = q.popleft()
if n.left:
q.append(n.left)
if n.right:
q.append(n.right)
res.append(n.val)
return res
二叉树展开为链表
class Solution:
node = None # 类变量,用于跟踪链接链表的当前节点
def flatten(self, root: Optional[TreeNode]) -> None:
"""
不返回任何内容,而是就地修改 root
"""
if not root: # 如果当前节点为空,直接返回
return
if not self.node: # 如果 self.node 为空,初始化为当前 root
self.node = root
else:
self.node.left = None # 将当前节点的左子指针设为空
self.node.right = root # 将当前节点的右子指针指向当前的 root
self.node = self.node.right # 更新 self.node 为新的右子节点
# 保存右子树的引用,因为递归过程中右子树会被覆盖
r = root.right
# 递归展平左子树
self.flatten(root.left)
# 递归展平右子树(注意这里传入的是保存在 r 中的原来的右子树)
self.flatten(r)
从前序和中序遍历序列构造二叉树
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# 如果前序遍历序列为空,返回 None(递归终止条件)
if not preorder:
return None
# 找到根节点在中序遍历中的位置
left_size = inorder.index(preorder[0]) # 根节点(前序遍历第一个元素)在中序遍历中的索引,它划分了左子树和右子树
# 递归构建左子树
left = self.buildTree(
preorder[1:1 + left_size], # 左子树的前序遍历序列
inorder[:left_size] # 左子树的中序遍历序列
)
# 递归构建右子树
right = self.buildTree(
preorder[1 + left_size:], # 右子树的前序遍历序列
inorder[1 + left_size:] # 右子树的中序遍历序列
)
# 返回构建的树的根节点
return TreeNode(
preorder[0], # 当前树的根节点值(前序遍历的第一个值)
left, # 构建好的左子树
right # 构建好的右子树
)
路径总和
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
# 内部函数,用于计算从当前节点开始,和为targetSum的所有路径数
def rootSum(root, targetSum):
if root is None:
return 0 # 如果当前节点为空,返回0(代表没有路径)
ret = 0
if root.val == targetSum:
ret += 1 # 如果当前节点值等于目标和,路径+1
# 递归计算左子树的路径数
ret += rootSum(root.left, targetSum - root.val)
# 递归计算右子树的路径数
ret += rootSum(root.right, targetSum - root.val)
return ret # 返回从当前节点开始的所有路径数
if root is None:
return 0 # 如果根节点为空,返回0
# 计算从当前根节点开始的路径和
ret = rootSum(root, targetSum)
# 递归计算左子树中的路径和
ret += self.pathSum(root.left, targetSum)
# 递归计算右子树中的路径和
ret += self.pathSum(root.right, targetSum)
return ret # 返回所有路径的总数
二叉树的最近公共祖先
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 如果当前节点是None,或者是节点p或q中的任意一个,直接返回当前节点
if root in (None, p, q):
return root
# 递归查找左子树中的LCA
left = self.lowestCommonAncestor(root.left, p, q)
# 递归查找右子树中的LCA
right = self.lowestCommonAncestor(root.right, p, q)
# 如果左子树和右子树都找到了对应的节点,说明当前节点是LCA
if left and right:
return root
# 返回找到的非空子树的结果
return left or right
二叉树中的最大路径和
class Solution:
def maxPathSum(self, root: TreeNode, ok=True) -> int:
if not root:
return 0 # 如果当前节点为空,返回0(递归终止条件)
# 递归计算左子树和右子树的最大路径和(不允许跨越根节点)
left = self.maxPathSum(root.left, False)
right = self.maxPathSum(root.right, False)
# 计算经过当前节点的路径和,并更新全局最大路径和
self.maxSum = max(getattr(self, 'maxSum', float('-inf')), left + root.val + right)
# 当从根节点开始时返回最大路径和,否则只返回单侧最大路径和
return self.maxSum if ok else max(root.val + max(left, right), 0) # 这里的root.val 和 上一行的 root.val 不是同一个
对于最初的调用(ok=True),返回 self.maxSum,表示整个树中的最大路径和。
对于递归调用(ok=False),返回的是从当前节点出发的最大单侧路径和(大于等于0)。