Leetcode
最简单的一种是用层序遍历。BFS
递归的话也是可以的,只是稍微有些复杂。使用两个全局变量maxDepth
和res
,记录最大深度下的叶子节点的值。
因为我们遍历的时候先遍历左枝,所以在最大深度记录的第一个叶子节点就是左下角的值。
递归
# 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 findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
maxDepth = float("-inf")
res = 0
def dfs(root, depth):
nonlocal maxDepth
nonlocal res
if not root.left and not root.right:
if depth > maxDepth:
maxDepth = depth
res = root.val
return
if root.left:
dfs(root.left, depth + 1)
if root.right:
dfs(root.right, depth + 1)
dfs(root, 0)
return res
层序遍历
# 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 findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
q = deque([root])
while q:
level = []
for i in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return level[0]
nonlocal
。Python中的变量有两个部分:变量的名称和它的值。对于可变对象,我们可以直接修改它的值,而不需要改变变量的引用。对于不可变对象,我们需要使用nonlocal
关键字来修改它的值。这就是为什么在嵌套函数中,我们可以直接修改列表或字典,但不能直接修改数字或字符串的原因。self.maxDepth, self.res
的方式,这样子变量会变成全局变量。Leetcode
具体的思路是前序遍历,并记录下遍历到叶节点的总和,如果等于targetSum
则返回True
。
一开始我的写法如下:
# 第一份代码
# 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 hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
def helper(node, total, target):
if not node.left and not node.right:
temp = total+node.val
if temp == target:
return True
if node.left:
helper(node.left, total + node.val, target)
if node.right:
helper(node.right, total + node.val, target)
return True if helper(root, 0, targetSum) else False
我跑的时候发现,结果只会return False,不会return True。但是我在print(temp)
之后发现遍历的值没有什么问题。而且我尝试打印出来helper(root, 0, targetSum)
,结果是None
。
于是我加入了一个全局变量res
来储存这个True
结果。
# 第二份,加入nonlocal res
# 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 hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
res = False
def helper(node, total, target):
nonlocal res
if not node.left and not node.right:
temp = total+node.val
if temp == target:
res = True
if node.left:
helper(node.left, total + node.val, target)
if node.right:
helper(node.right, total + node.val, target)
helper(root, 0, targetSum)
return res
这下跑通了,但是为什么第一份代码不行呢?
我在问了chatGPT之后发现,跑不通的原因是,我递归得到的这份True
,并不能传回到上一级,上一级没有return任何东西,所以传回到上一级之后,还是return None。所以我做出了如下修改,这下能跑了:
# 第三份,为第一份的修改版
# 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 hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
def helper(node, total, target):
if not node.left and not node.right:
temp = total + node.val
if temp == target:
return True
left_res = helper(node.left, total + node.val, target) if node.left else False
right_res = helper(node.right, total + node.val, target) if node.right else False
return left_res or right_res
return helper(root, 0, targetSum)
这一份代码在最后会使用or
来串起递归的每一份return
,从而保证只要有一个True
,就能被主函数返回。
Leetcode
思路和前一题类似,但是路径总和ii要遍历整个树,找到所有路径,所以递归函数不需要返回值。
# 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 pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root:
return []
res = []
def helper(node, total, path, target):
path.append(node.val)
if not node.left and not node.right:
if total + node.val == target:
# 此处一定要是path.copy(), 否则传递进res的是一份引用
res.append(path.copy())
if node.left:
helper(node.left, total + node.val, path, target)
path.pop()
if node.right:
helper(node.right, total + node.val, path, target)
path.pop()
helper(root, 0, [], targetSum)
return res
res.append(path.copy())
这里记得需要使用path.copy()
,否则之后的path.pop()
也会影响到append到res
里面的path
。path
里面,传递变量的时候传递path[:]
即可。Leetcode
后序遍历会告诉我们根节点的位置,根据根节点的位置回到中序遍历可以找到左枝和右枝的位置。
后序遍历和中序遍历
# 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 buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
if not inorder:
return None
target = postorder.pop()
ind = inorder.index(target)
root = TreeNode(val = target) # 中
# 先root.right,因为后序遍历的顺序是左右中,倒过来就是中右左
root.right = self.buildTree(inorder[ind + 1:], postorder) #左
root.left = self.buildTree(inorder[:ind], postorder)# 右
return root
前序遍历和中序遍历
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# 判定条件是inorder而不是preorder,因为inorder会于preorder先一步变空。
if not inorder:
return None
# 注意pop的位置,首位
ind = inorder.index(preorder.pop(0))
root = TreeNode(val = inorder[ind])
root.left = self.buildTree(preorder, inorder[:ind])
root.right = self.buildTree(preorder, inorder[ind + 1:])
return root
可以改进的方法,查询index的操作每次耗时O(n)
,可以用哈希表提前记录好index和value。同时我们注意到preorder或者postorder没必要放进循环变量里面,只需要放入inorder的头尾两个指针就好了。
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
idxDict = {v: i for i, v in enumerate(inorder)}
def helper(l, r):
if l > r:
return None
target = preorder.pop(0)
ind = idxDict[target]
root = TreeNode(val = inorder[ind])
root.left = helper(l, ind - 1)
root.right = helper(ind + 1, r)
return root
return helper(0, len(inorder) - 1)
这样子改完之后,时间从O(n^2)
降低到了O(n)
。