Day 18|513.找树左下角的值, 112. 路径总和, 113.路径总和ii, 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
513.找树左下角的值
思路
可以用层序遍历,返回层序遍历最后一行的第一个。
尝试层序遍历:
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
if not root:
return root
queue = deque([root])
result = []
while queue:
tmp = []
for _ in range(len(queue)):
node = queue.popleft()
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(tmp)
return result[-1][0]
成功通过!
递归遍历思路:
最底层,首先要用二叉树的最大深度判断。求最大深度要用前序遍历。
最左边,也就是遍历到的第一个符合最大深度的值。
不知道如何写代码
根据代码随想录:
要点:
- 本题用前中后序都可以,只要遍历时先遍历左边就好了
- 本题需要两个全局变量,maxDepth记录最大深度,result记录最大深度最左边的值。
- 递归函数的参数中,有个depth,记录当前深度
- 终止条件:遇到叶子节点,更新最大深度
- 单层递归需要用到回溯
- 本题没有中的处理逻辑
尝试写代码
class Solution:
result = 0
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
if not root:
return None
maxDepth = 0
# result = 0
self.traversal(root, 0, maxDepth, Solution.result)
return Solution.result
def traversal(self, node, depth, maxDepth, result):
if not node.left and not node.right:
if depth > maxDepth:
maxDepth = depth
result = node.val
if node.left:
depth += 1
self.traversal(node.left, depth, maxDepth, result)
depth -= 1
if node.right:
depth += 1
self.traversal(node.right, depth, maxDepth, result)
depth -= 1
最终结果为0,result的值没有改变。不知道如何处理全局变量。
Python注意:
- 全局变量用
self.xxx
最终代码:
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
if not root:
return None
self.maxDepth = float('-inf')
self.result = 0
self.traversal(root, 0)
return self.result
def traversal(self, node, depth):
if not node.left and not node.right:
if depth > self.maxDepth:
self.maxDepth = depth
self.result = node.val
return
if node.left:
depth += 1
self.traversal(node.left, depth)
depth -= 1
if node.right:
depth += 1
self.traversal(node.right, depth)
depth -= 1
112. 路径总和
思路
用targetsum的值减去遍历到的每个节点,如果到叶子几点,值为0,则返回True
问题:
如果遍历到叶子节点,符合题目要求,返回True,这个返回值要如何传递回去?是通过二叉树一层层返回,还是用一个变量专门记录?
使用全局变量result记录,如果遇到一个满足条件的结果,result = True。
没有中的处理逻辑,在处理左右逻辑时,用sum减去节点的值,并且用到回溯
尝试写代码:
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
self.result = False
self.traversal(root, targetSum)
return self.result
def traversal(self, node, sum):
if not node.left and not node.right:
if sum == 0:
self.result = True
return
if node.left:
sum -= node.left.val
self.traversal(node.left, sum)
sum += node.left.val
if node.right:
sum -= node.right.val
self.traversal(node.right, sum)
sum += node.right.val
测试用例通过,但如果只有一个节点,则错误。
是因为缺少对根节点的处理。
根据代码随想录视频:
要点:
- 递归解法首先确定遍历顺序,本题用前中后序都可以。
- 如果找到一条路径,立刻返回,因此函数有返回值,通过二叉树一路返回上去。函数中还需要一个计数参数count,放入目标值,一路减去遍历的节点值。
- 终止条件:叶子节点且count为0,返回True;叶子节点且count不为0,返回False
- 由于函数返回只需要一层层传递上去,左右逻辑中的递归函数调用也需要判断其返回结果,如果为True,则继续返回True。最后函数结尾,返回False。
- 注意根节点的值要首先剪掉。
最终代码:
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
return self.traversal(root, targetSum - root.val)
def traversal(self, node, sum):
if not node.left and not node.right and sum == 0:
return True
elif not node.left and not node.right and sum != 0:
return False
if node.left:
sum -= node.left.val
if self.traversal(node.left, sum):
return True
sum += node.left.val
if node.right:
sum -= node.right.val
if self.traversal(node.right, sum):
return True
sum += node.right.val
return False
总结
自己的思路基本上想到了递归的顺序,想到了回溯的过程,甚至想到了直接用给定值减去遍历的节点,只是对于返回值的处理还需要整理。因为本题只要有一个答案就可以返回True,因此不需要遍历全部的二叉树,所以遇到满足条件的,直接返回True就行。此外,需要了解递归过程中的首尾值的处理,这里的单层递归逻辑中,没有处理到根节点,需要在一开始处理。
113.路径总和ii
思路
在上一题的基础上,新增一个函数参数path,用于记录遍历的路径。
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root:
return []
self.result = []
self.traversal(root, targetSum - root.val, [root.val])
return self.result
def traversal(self, node, count, path):
if not node.left and not node.right and count == 0:
self.result.append(path.copy())
return
if not node.left and not node.right:
return
if node.left:
count -= node.left.val
path.append(node.left.val)
self.traversal(node.left, count, path)
count += node.left.val
path.pop()
if node.right:
count -= node.right.val
path.append(node.right.val)
self.traversal(node.right, count, path)
count += node.right.val
path.pop()
return
成功通过!
只是在记录path的过程中,如果只是result.append(path)
,那result的值会跟着path动态改变。
改变:需要加入path的全部值,注意代码细节,应写为result.append(path[:])
根据代码随想录:
注意:
- 熟练运用python的类的特性,如果一个参数,类中的多个方法都用到了,就用self设为类内全局变量或成员变量。不需要设置为函数参数。
- 不知道为什么,需要写成这样的形式:
result.append(path[:])
,原先的就不行。?? - 答:Python里不写[:]代表的是传的是引用,会变,带上就是拷贝的结果,相当于快照
最终代码:
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root:
return []
self.result = []
self.path = [root.val]
self.traversal(root, targetSum - root.val)
return self.result
def traversal(self, node, count):
if not node.left and not node.right and count == 0:
self.result.append(self.path[:])
return
if not node.left and not node.right:
return
if node.left:
count -= node.left.val
self.path.append(node.left.val)
self.traversal(node.left, count)
count += node.left.val
self.path.pop()
if node.right:
count -= node.right.val
self.path.append(node.right.val)
self.traversal(node.right, count)
count += node.right.val
self.path.pop()
return
106.从中序与后序遍历序列构造二叉树
思路
首先根据后序,能判断出根节点就是后序遍历中的最后一个值,然后在中序遍历中寻找这个值,并以此为分界,左边的是左子树,右边是右子树。
然后递归,再从后序找到左子树/右子树的根节点,重复以上过程。
前序遍历写递归。
问题
题目说返回二叉树,是要返回输出中包含null的列表,还是只需要返回根节点?
如果返回根节点,那二叉树中的各个节点该如何构造
尝试以返回根节点写代码
具体结合后序遍历与中序遍历,分离出左/右子树的中后序遍历。
终止条件:如果左右的中/后序遍历列表为空
尝试写代码:
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
root = TreeNode(-1)
self.traversal(root, inorder, postorder)
return root
def traversal(self, node, inorder, postorder):
if not inorder and not postorder:
return
node.val = postorder[-1]
index = inorder.index(node.val)
left_inorder = inorder[:index]
right_inorder = inorder[index + 1:]
left_postorder = postorder[:len(left_inorder)]
right_postorder = postorder[len(left_inorder):len(right_inorder) + 1]
self.traversal(node.left, left_inorder, left_postorder)
self.traversal(node.right, right.inorder, right_postorder)
出错,node.val赋值出错。
不知道如何给新的二叉树赋值。
根据代码随想录视频:
要点:
- 后续数组为0,空节点
- 后续数组最后一个元素为节点元素
- 寻找中序数组位置,做切割点
- 切中序数组
- 切后续数组
- 递归处理左区间右区间
最终代码:
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
if not postorder:
return None
root_val = postorder[-1]
root = TreeNode(root_val)
separator_idx = inorder.index(root_val)
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
postorder_left = postorder[:len(inorder_left)]
postorder_right = postorder[len(inorder_left): len(postorder) - 1]
root.left = self.buildTree(inorder_left, postorder_left)
root.right = self.buildTree(inorder_right, postorder_right)
return root
Python中list的查找方法
in
:判断是否在列表中count()
:统计给定值在列表中出现的次数index
:查看给定值在列表中的位置
105.从前序与中序遍历序列构造二叉树
思路
构造二叉树的细节与上一题相似,只是划分中序遍历区间时,利用前序遍历。
最终代码:
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder:
return None
root_val = preorder[0]
root = TreeNode(root_val)
separator_idx = inorder.index(root_val)
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
preorder_left = preorder[1:1 + len(inorder_left)]
preorder_right = preorder[1 + len(inorder_left):]
root.left = self.buildTree(preorder_left, inorder_left)
root.right = self.buildTree(preorder_right, inorder_right)
return root