222.完全二叉树的节点个数
解题思路:用后序解题
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
# 左 右 中
leftCount = self.countNodes(root.left)
rightCount = self.countNodes(root.right)
return leftCount + rightCount + 1
解题思路:利用完全二叉树的原理去解答
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
# 给定完全二叉树--> 从上到下找满二叉树(2^n - 1) + 子树个数
# 如果左侧深度==右侧深度,这是个满二叉树
leftNode = root.left
rightNode = root.right
leftDepth, rightDepth =0,0
while leftNode:
leftNode = leftNode.left
leftDepth +=1
while rightNode:
rightNode = rightNode.right
rightDepth += 1
#如果左侧深度等于右侧深度,则整个子树是一个满二叉树。
#在这种情况下,节点总数为 2^(leftDepth + 1) - 1
if leftDepth == rightDepth:
return 2**(leftDepth+1) - 1
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
110.平衡二叉树
题目:左右子树高度差<=1
解题思路:递归的办法
1. isBalanced 方法:
• 直接调用 getHeight 方法,如果返回值为 -1,表示树不平衡;否则树平衡。
2. getHeight 方法(递归获取子树高度,并检查平衡性):
• 终止条件:如果 node 为 None,返回高度 0。
• 递归获取左右子树高度:
• leftHeight 是左子树的高度。如果返回 -1,表示左子树已经不平衡,直接返回 -1。
• rightHeight 是右子树的高度。如果返回 -1,表示右子树已经不平衡,直接返回 -1。
• 检查平衡性:
• 如果左右子树高度差超过 1,返回 -1 表示不平衡。
• 返回节点高度:
• 如果左右子树平衡,则返回 1 + max(leftHeight, rightHeight),表示当前节点的高度。
• 时间复杂度:O(n),因为每个节点只访问一次。
• 空间复杂度:O(h),其中 h 是树的高度,用于递归调用栈。
class Solution:
def isBalanced(self, root: Optional[TreeNode]) -> bool:
return self.getHeight(root) != -1
def getHeight(self,node):
if not node: return 0
leftHeight = self.getHeight(node.left)
if leftHeight == -1: return -1
rightHeight = self.getHeight(node.right)
if rightHeight == -1: return -1
if abs(rightHeight - leftHeight) > 1 : return -1
return 1 + max(rightHeight, leftHeight)
解题思路:迭代。用一个栈 stack 存储节点和它的访问状态,每个元素是一个 (node, visited) 元组。visited 标记是否已经处理过该节点的左右子树。
class Solution:
def isBalanced(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
# 栈存储元组 (节点, 是否已访问)
stack = [(root, False)]
# 字典存储每个节点的高度
height = {None: 0}
while stack:
node, visited = stack.pop()
if node is None:
continue
if visited:
# 已经访问过该节点,计算该节点的高度
leftHeight = height[node.left]
rightHeight = height[node.right]
# 如果左右子树高度差超过 1,返回 False
if abs(leftHeight - rightHeight) > 1:
return False
# 记录该节点的高度
height[node] = 1 + max(leftHeight, rightHeight)
else:
# 第一次遇到该节点,将其标记为已访问
stack.append((node, True))
# 先加入右子节点,再加入左子节点
stack.append((node.right, False))
stack.append((node.left, False))
return True
257. 二叉树的所有路径
解题思路:使用递归遍历二叉树的所有路径,path 列表用于记录当前路径,result 列表用于存储所有完整路径。
叶节点处理:如果当前节点没有左子树和右子树(是叶节点),将 path 转换为路径字符串并添加到 result 中。
回溯:path.pop() 在递归调用完成后执行,确保每条路径都能恢复到上一级状态,以便不影响其他路径。
class Solution:
def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
result = []
path = []
if not root:
return result
self.traversal(root, path, result)
return result
def traversal(self, node, path, result):
path.append(node.val)
# 如果当前节点是叶子节点,将路径加入结果中
if not node.left and not node.right:
sPath = '->'.join(map(str, path))
result.append(sPath)
# 如果存在左子树,继续遍历左子树
if node.left:
self.traversal(node.left, path, result)
# 如果存在右子树,继续遍历右子树
if node.right:
self.traversal(node.right, path, result)
# 回溯,弹出当前节点,恢复上一层的路径
path.pop()
隐藏回溯:递归调用时传入 path[:](即 path 的切片副本)来实现“隐藏”回溯。使用 path[:] 会生成一个新的列表副本,因此每次递归调用时修改 path 不会影响原始的 path 列表,从而实现了不需要显式回溯的效果。
from typing import List, Optional
class Solution:
def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
if not root:
return []
result = []
self.traversal(root, [], result)
return result
def traversal(self, cur: TreeNode, path: List[int], result: List[str]) -> None:
if not cur:
return
path.append(cur.val)
# 如果是叶子节点,加入结果
if not cur.left and not cur.right:
result.append('->'.join(map(str, path)))
# 递归遍历左子树和右子树,传入 path 的副本
if cur.left:
self.traversal(cur.left, path[:], result)
if cur.right:
self.traversal(cur.right, path[:], result)
404.左叶子之和
class Solution:
def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
# 递归调用左子树
leftValue = self.sumOfLeftLeaves(root.left)
# 检查当前节点的左子节点是否为叶子节点
if root.left and not root.left.left and not root.left.right:
leftValue += root.left.val
# 递归调用右子树
rightValue = self.sumOfLeftLeaves(root.right)
return leftValue + rightValue