LeetCodeCampsDay16

记录下,今儿三个题目一次过;

其中路径总和问题和之前求二叉树的所有路径相似

找树左下角的值可以用层序解决

从中序与后序遍历序列构造二叉树 可以用递归方法,找到输入输出;终止条件;单层逻辑就能解决

找树左下角的值

https://leetcode.cn/problems/find-bottom-left-tree-value/

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

img

1
2
输入: root = [2,1,3]
输出: 1

示例 2:

img

1
2
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,104]
  • -231 <= Node.val <= 231 - 1

层序迭代思路

  1. 使用迭代–层序的方法, 令res存储每层第一个元素,遍历到最后一层结束,再返回res即可

层序迭代代码

  • 时间复杂度O(N)
  • 空间复杂度O(W)—二叉树最大宽度(也即二叉树每层最大长度)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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:
# 使用迭代--层序的方法, 令res存储每层第一个元素,遍历到最后一层结束,再返回res即可
from collections import deque
que = deque()
if not root:
return
que.append(root)
while que:
L = len(que)
level = None
for i in range(L):
node = que.popleft()
if i ==0:
level = node.val
if node.left:
que.append(node.left)
if node.right:
que.append(node.right)
return level

112. 路径总和

https://leetcode.cn/problems/path-sum/

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

示例 1:

img

1
2
3
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

img

1
2
3
4
5
6
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

1
2
3
输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

后序迭代思路

  1. 这题目和“二叉树所有路径”(day15)相似,只是将保存路径变成保存路径上所有元素的和;遇到叶子节点时,判断这和是否与target相等即可
  2. 本题同样对前/中/后,甚至逆向的前/中/后顺序不敏感,所有顺序都能通过
  3. 下面代码使用后序

后序迭代代码

  • 时间复杂度O(N)
  • 空间复杂度O(H)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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:
# 使用后序遍历+栈?
stack = list()
path = list()
if not root:
return False
stack.append(root)
path.append(root.val)
while stack:
node = stack.pop()
pathNode = path.pop()
# 终止条件,遇到叶子节点了
if not node.left and not node.right:
if pathNode == targetSum:
return True

if node.right:
stack.append(node.right)
path.append(pathNode + node.right.val)
if node.left:
stack.append(node.left)
path.append(pathNode + node.left.val)

return False

递归思路

  1. 输入输出:输入节点和targetSum,这里的targetSum每次都需要变小; 输出True/False
  2. 终止条件:不能是判断node是否为空,因为它说明不了什么;需要判断是否为叶子节点,并且判断targetSum是否为零,为零则返回True;如果叶子节点且targetSum不为零,返回False
  3. 单层逻辑,判断左、右子树是否已经存在了满足target的路径,存在就返回True

递归代码

  • 时间复杂度O(N)
  • 空间复杂度O(N)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 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 foo(self, node: TreeNode, targetSum: int) -> bool:
# 终止条件不能是判断node是否为空,因为它说明不了什么
# 需要判断是否为叶子节点,并且判断targetSum是否为零
if not node.left and not node.right and 0 == targetSum:
return True
# 如果叶子节点且targetSum不为零,返回False
if not node.left and not node.right:
return False

if node.left:
# 每一次都需要判断是否它的左子树已经找到了targetSum为零的情况
if self.foo(node.left, targetSum - node.left.val):
return True
if node.right:
# 每一次都需要判断是否它的右子树已经找到了targetSum为零的情况
if self.foo(node.right, targetSum - node.right.val):
return True

return False

def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
#
if not root:
return False
return self.foo(root, targetSum)

113. 路径总和 II

https://leetcode.cn/problems/path-sum-ii/

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

示例 1:

img

1
2
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:

img

1
2
输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:

1
2
输入:root = [1,2], targetSum = 0
输出:[]

提示:

  • 树中节点总数在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

思路

  1. 和之前做的差不多,但注意,这里是将pathNode + [node.right.val] 传进去path里,所以对pathNode本身没有修改,如果直接修改pathNode,会把所有路径的值都添加进去,就不对了

迭代代码

  • 时间复杂度O(N^2)
  • 空间复杂度O(N^2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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]]:
stack = list()
path = list()
res = list()
if not root:
return []
stack.append(root)
path.append([root.val])
while stack:
# print(path)
node = stack.pop()
# 先把节点从path中弹出
pathNode = path.pop()
# 为了实现中、前、后,入栈需要使用中、后、前
if not node.left and not node.right:
if sum(pathNode) == targetSum:
res.append(pathNode)

if node.right:
stack.append(node.right)
# 注意,这里是将pathNode + [node.right.val]传进去,所以对pathNode本身没有修改
# 如果直接修改pathNode,会把所有路径的值都添加进去,就不对了
path.append(pathNode + [node.right.val])

if node.left:
stack.append(node.left)
path.append(pathNode + [node.left.val])

return res

递归代码

  • 时间复杂度O(N)
  • 空间复杂度O(N)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 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 __init__(self):
self.res = list()
self.path = list()

def foo(self, node: TreeNode, targetSum: int):
# 终止条件不能是判断node是否为空,因为它说明不了什么
# 需要判断是否为叶子节点,并且判断targetSum是否为零
# print(self.res, self.path)
if not node.left and not node.right and targetSum == 0:
print(self.res, self.path, )
self.res.append(self.path[:])
return

# 如果叶子节点且targetSum不为零,返回False
if not node.left and not node.right:
return

if node.left:
# 每一次都需要判断是否它的左子树已经找到了targetSum为零的情况
self.path.append(node.left.val)
self.foo(node.left, targetSum - node.left.val)
self.path.pop()

if node.right:
# 每一次都需要判断是否它的右子树已经找到了targetSum为零的情况
self.path.append(node.right.val)
self.foo(node.right, targetSum - node.right.val)
self.path.pop()

return

def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
self.res.clear()
self.path.clear()
if not root:
return self.res
self.path.append(root.val)
self.foo(root, targetSum - root.val)
return self.res

106. 从中序与后序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:

img

1
2
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

1
2
输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorderpostorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

递归思路

  1. 用两个指针,一个指针指向inorder首,一个指向postorder末尾
  2. 使用while循环,当left.val == right.val时
    1. 右子树
    2. left的下标也是右子树的postorder的开始下标
    3. right - 1的下标是右子树postorder的结束下标(注意python里列表的右是开区间,所以写postorder[left: right]即可)
    4. left + 1 是inorder中右子树开始下标,并且后面全是右子树
    5. 左子树
    6. left 是inorder中左子树结束下标,并且前面全是左子树
    7. left 是postorder中左子树结束下标,并且前面全是左子树

把左子树想成一大块儿;右子树想成一大块儿,就好理解了

in 左子树 左子树 3 右子树 右子树 右子树
Post 左子树 左子树 右子树 右子树 右子树 3
left Right

这是对于3的两个子树的区间位置

1
2
leftTree = self.foo(inorder[:left], postorder[:left])
rightTree = self.foo(inorder[left + 1:], postorder[left: right])

递归代码

  • 时间复杂度O(N^2)—最好情况O(NlogN)
  • 空间复杂度O(N^2)
  • 可以将while找root替换成index函数,即left = inorder.index(postorder[right])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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 foo(self, inorder: List[int], postorder: List[int]) -> TreeNode:
if len(inorder) == 0:
return None
left = 0
right = len(postorder) - 1
while inorder[left] != postorder[right]:
left += 1

leftTree = self.foo(inorder[:left], postorder[:left])
rightTree = self.foo(inorder[left + 1:], postorder[left: right])

node = TreeNode(inorder[left])
node.left = leftTree
node.right = rightTree

return node

def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
#
return self.foo(inorder, postorder)

105. 从前序与中序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

img

1
2
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

1
2
输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorderinorder无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

代码

  • 时间复杂度O(N)—最好情况O(logN)
  • 空间复杂度O(N)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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 foo(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if len(preorder) == 0:
return None

left = 0
right = inorder.index(preorder[left])

leftTree = self.foo(preorder[left + 1: right + 1], inorder[:right])
rightTree = self.foo(preorder[right + 1:], inorder[right + 1:])
root = TreeNode(preorder[left])
root.left = leftTree
root.right = rightTree

return root

def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
return self.foo(preorder, inorder)