一、深度优先遍历
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 root is None:
return 0
l_depth = self.maxDepth(root.left)
r_depth = self.maxDepth(root.right)
return max(l_depth,r_depth)+1
另一种思路:维护一个全局变量,不在遍历时返回,在遍历时直接更新全局变量
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
ans = 0
def f(node,cnt):
if node is None:
return
cnt += 1
nonlocal ans
ans = max(ans,cnt)
f(node.left,cnt)
f(node.right,cnt)
f(root,0)
return ans
思路:对于最小深度有两种特殊情况:如果root是空,那最小深度是0,如果root没有左右子树,那最小深度是1.假设该树有左子树,那计算叶子节点的最小深度。假设该树有右子树,就计算右子树的最小深度。用全局变量存储最小深度,每次查看是否比当前值小再更新。
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if root is None: #如果root是空,那最小深度是0
return 0
if root.left is None and root.right is None: #如果root没有左右子树,那最小深度是1
return 1
mindepth = inf
if root.left: #用全局变量存储最小深度,每次查看是否比当前值小再更新。
mindepth = min(self.minDepth(root.left),mindepth)
if root.right:
mindepth = min(self.minDepth(root.right),mindepth)
return mindepth + 1
思路:从根节点开始,用目标值减去当前的val如果一直到叶子节点,目标值与当前叶子的val相等了,则存在这个路径。两个边界条件:(1)root是空的时候,返回FALSE(2)当root的左右子树都是空的时候,判断现在root的val和目标值是否相等。递归条件:传入左右子树和目标值减去当前的val。
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None:
return False
if root.left is None and root.right is None:
return targetSum == root.val
return self.hasPathSum(root.left, targetSum-root.val) or self.hasPathSum (root.right, targetSum - root.val)
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
ans = []
path = []
def f(root,targetSum):
nonlocal ans,path
if root is None: #边界条件
return
path.append(root.val)
if root.left is None and root.right is None and root.val == targetSum:
ans.append(path.copy()) #到达叶子节点,找到路径
f(root.left,targetSum-root.val)
f(root.right,targetSum-root.val)
path.pop() #回溯的时候把当前值pop掉
f(root,targetSum)
return ans
思路二:直接用递归,每次的路径也是递归过程中传递下去的
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
ans = []
def f(root,targetSum,path):
nonlocal ans
if root is None:
return
if root.left is None and root.right is None and root.val == targetSum:
ans.append(path+[root.val])
f(root.left,targetSum-root.val,path+[root.val])
f(root.right,targetSum-root.val,path+[root.val])
f(root,targetSum,[])
return ans
思路:每个节点都对应一个数字,等于其父节点对应的数字乘以10再加上该节点的值(这里假设根节点的父节点对应的数字是 0)。只要计算出每个叶子节点对应的数字,然后计算所有叶子节点对应的数字之和,即可得到结果。
class Solution:
def sumNumbers(self, root: Optional[TreeNode]) -> int:
#计算出每个叶子节点的值,相加就是全部数字之和。
def f(root,s):
if root is None:
return 0
s = s*10 + root.val
if root.left is None and root.right is None:
return s
return f(root.left,s) + f(root.right,s)
return f(root,0)
思路:用递归,传路径。
class Solution:
def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
ans = []
def f(root,path):
nonlocal ans
if root is None:
return
if root.left is None and root.right is None:
ans.append(path+str(root.val))
f(root.left,path+str(root.val)+'->')
f(root.right,path+str(root.val)+'->')
f(root,'')
return ans
思路:边界条件:如果遍历到p和q某一个为空了,就不会继续遍历了,如果p is q,那就返回true,否则返回FALSE。然后递归p和q的值是否相同,以及两颗树的左子树,和两棵树的右子树。
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if q is None or p is None:
return p is q
return q.val == p.val and self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)
思路:看根节点的左右子树是否轴对称,那就是q的左子树应该等于p的右子树。
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
def f(p,q):
if p is None or q is None:
return p is q
return p.val == q.val and f(p.left,q.right) and f(p.right,q.left)
return f(root.left,root.right)
思路:我们去计算两个子树的高度,然后通过高度差来判断是否平衡。如果不平衡,就让高度差为-1,然后不断返回-1到递归的入口。最后判断拿到的值是否是-1,不是就返回true,否则就返回FALSE。
class Solution:
def isBalanced(self, root: Optional[TreeNode]) -> bool:
def get_height(node):
if node is None:
return 0
left_height = get_height(node.left)
if left_height == -1:
return -1
right_height = get_height(node.right)
if right_height == -1 or abs(right_height-left_height) > 1:
return -1
return max(left_height,right_height) + 1 #树的高度
return get_height(root) != -1
思路:维护一个ans,先递归右子树,当ans的长度小于该节点的的高度时,就把答案写进去,如果大于就不写,然后再递归左子树。
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def f(root,depth):
if root is None:
return
if depth == len(ans): #增加答案
ans.append(root.val)
f(root.right,depth+1)
f(root.left,depth+1)
f(root,0)
return ans
思路: #递归左右子树调换,直到为叶子节点或者为空
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None or (root.left is None and root.right is None):
return root
t = root.left
root.left = root.right
root.right = t
self.invertTree(root.left)
self.invertTree(root.right)
return root
思路一:计算在该路径下的祖先的最大值和最小值。然后用该值和当前节点的差值以及记录的值作比较,取最大。当前最大值和最小值随节点变动,在递归时传入。
class Solution:
def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
ans = 0
def f(root,min_num,max_mun):
nonlocal ans
if root is None:
return
min_num = min(root.val,min_num)
max_mun = max(root.val,max_mun)
ans = max(ans,root.val - min_num,max_mun - root.val)
f(root.left,min_num,max_mun)
f(root.right,min_num,max_mun)
f(root,root.val,root.val)
return ans
思路二:从根节点的角度来说,我只需要记录这条路径下的最大值和最小值,在递归回根节点的时候,计算差值即可。
class Solution:
def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
ans = 0
def f(root,min_mun,max_num):
nonlocal ans
if root is None:
ans = max(ans,max_num - min_mun)
return
max_num = max(root.val,max_num)
min_mun = min(root.val,min_mun)
f(root.left,min_mun,max_num)
f(root.right,min_mun,max_num)
f(root,root.val,root.val)
return ans
思路:
class Solution:
def longestZigZag(self, root: Optional[TreeNode]) -> int:
ans = 0
def f(node,l,r):
nonlocal ans
if node is None:
return
ans = max(ans,l,r) #维护此时的最大值
if node.left: f(node.left,r+1,0) #往左子树走,左值是父亲节点的r+1,右值是0
if node.right: f(node.right,0,l+1)
f(root,0,0)
return ans
class Solution:
def sufficientSubset(self, root: Optional[TreeNode], limit: int) -> Optional[TreeNode]:
limit -= root.val
if root.left is None and root.right is None: #叶子节点
return None if limit > 0 else root
if root.left: root.left = self.sufficientSubset(root.left,limit)
if root.right: root.right = self.sufficientSubset(root.right,limit)
return root if root.left or root.right else None
class Solution:
def delNodes(self, root: Optional[TreeNode], to_delete: List[int]) -> List[TreeNode]:
ans = []
def f(root,to_delete):
nonlocal ans
if root is None:
return None
#后序遍历,在删根节点的时候,已经可以拿到左右子树了。
root.left = f(root.left,to_delete) #更新左儿子为递归左子树之后的结果
root.right = f(root.right,to_delete) #更新右儿子为递归右子树之后的结果
if root.val not in to_delete: #如果该点不在列表里,不需要删除
return root
if root.left: ans.append(root.left) #需要删除该点时,有左子树,左子树入队
if root.right:ans.append(root.right) #有右子树,右子树入队
return None #最后将该点置位空返回
if f(root,to_delete): ans.append(root) #在调用根节点之后,如果不是空,就把根节点入队
return ans