导语
leetcode刷题笔记记录,本篇博客记录二叉树部分的题目,主要题目包括:
- 102.二叉树的层序遍历(opens new window)
- 107.二叉树的层次遍历II(opens new window)
- 199.二叉树的右视图(opens new window)
- 637.二叉树的层平均值(opens new window)
- 429.N叉树的层序遍历(opens new window)
- 515.在每个树行中找最大值(opens new window)
- 116.填充每个节点的下一个右侧节点指针(opens new window)
- 117.填充每个节点的下一个右侧节点指针II(opens new window)
- 104.二叉树的最大深度(opens new window)
- 111.二叉树的最小深度
- 226 翻转二叉树
- 101 对称二叉树
- 100. 相同的树
- 572. 另一棵树的子树
二叉树的层次遍历
上面的前10道题目都是层次遍历的题目及其变种,这里先看一下层次遍历的代码:
Leetcode 102.二叉树的层次遍历
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
queue = deque()
if root:
queue.append(root)
while queue:
items = []
for i in range(len(queue)):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(items)
return result
使用collections中的deque构造双向队列,首先将root入队,接着进入while循环,循环的终止条件就是队列为空。然后进入for循环,逐个出队,每出队一个元素,就把该元素的左孩子和右孩子分别入队。每一层的元素放到一个列表中,之后再将该层元素append到最终的完整结果中。
基于以上模板,其他题目也很容易做出来
Leetcode 107.二叉树的层次遍历II
这道题目直接反向最终result即可。
from collections import deque
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
queue = deque()
result = []
if root:
queue.append(root)
while queue:
items = []
for i in range(len(queue)):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(items)
return result[::-1]
Leetcode 199.二叉树的右视图
类似的,这道题目直接取每行的最后一个元素。
from collections import deque
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
result = []
queue = deque()
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(items[-1])
return result
Leetcode 637.二叉树的层平均值
类似的,这道题目直接取每行的平均值。
from collections import deque
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
result = []
queue = deque()
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(items[-1])
return result
Leetcode 429.N叉树的层序遍历
这道题目在判断孩子节点是否为空时需要修改一下,变成for循环。
from collections import deque
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
result = []
queue = deque()
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
items.append(node.val)
if node.children:
for n in node.children:
queue.append(n)
result.append(items)
return result
Leetcode 515.在每个树行中找最大值
类似的,取每行的最大值即可。
from collections import deque
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
result = []
queue = deque()
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(max(items))
return result
Leetcode 116.填充每个节点的下一个右侧节点指针
这道题目返回值还是原来的树,不过需要注意的几个点如下:
- next本身就是空的;
- popleft出队后,队首元素就是当前出队元素的next,直接连接即可。
"""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
from collections import deque
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
queue = deque()
if root:
queue.append(root)
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
if i < size - 1:
node.next = queue[0]
if node.left:
queue.append(node.left)
queue.append(node.right)
return root
Leetcode 117.填充每个节点的下一个右侧节点指针II
与上面的类似,只不过不是完美二叉树,所以需要多一个判断条件if node.right。
from collections import deque
class Solution:
def connect(self, root: 'Node') -> 'Node':
queue = deque()
if root:
queue.append(root)
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
if i < size - 1:
node.next = queue[0]
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return root
Leetcode 104.二叉树的最大深度
这个只需要判断大循环(外层的while循环了几次就行),代码如下:
from collections import deque
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
cnt = 0
queue = deque()
if root:
queue.append(root)
while queue:
size = len(queue)
for _ in range(size):
node = queue.popleft()
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
cnt += 1
return cnt
Leetcode 111.二叉树的最小深度
这个最小深度,实际上也是大循环的次数,但是要求碰到一个节点的左右孩子都为空时直接跳出大循环。
from collections import deque
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
cnt = 0
queue = deque()
if root:
queue.append(root)
while queue:
size = len(queue)
cnt += 1
for _ in range(size):
node = queue.popleft()
if node.left is None and node.right is None:
return cnt
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return cnt
Leetcode 226 翻转二叉树
题目描述
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]
解法
这道题目可以采用递归或者层次遍历、迭代等多种方法解决。下面介绍递归和层次遍历法:
递归
递归可以采用前序遍历、后序遍历和中序遍历(不推荐),其主要实现如下:
# 前序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return root
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
# 后序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return root
self.invertTree(root.left)
self.invertTree(root.right)
root.left, root.right = root.right, root.left
return root
采用中序遍历时,一个主要的坑在于如果还是按上述写法更改交换位置的顺序,那么会重复操作某一侧的子树,为此,中序遍历应该操作同一侧,代码如下:
# 中序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return root
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(root.left)
return root
同样的,这道题目也可以通过层次遍历来解决,其核心思想就是层次遍历的过程中,交换节点的左右孩子,对所有的节点都这么做。
from collections import deque
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
queue = deque()
if root:
queue.append(root)
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
node.left, node.right = node.right, node.left
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return root
Leetcode 101 对称二叉树
题目描述
给你一个二叉树的根节点 root , 检查它是否轴对称。
示例 1:
输入: root = [1,2,2,3,4,4,3]
输出: true
解法
首先介绍递归解法,观察图片可以看出,如果我们要比较对称,那么一定是要比较左侧节点的左孩子与右侧节点的右孩子相等,右侧节点的左孩子和左侧节点的右孩子相等。所以,定义递归函数的输入是一个left节点和一个right节点,递归的终止条件则是left和right为空,代码如下:
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if root is None:
return True
else:
return self.compare(root.left, root.right)
def compare(self, left, right):
if left is None and right is None:
return True
elif left is None and right is not None:
return False
elif left is not None and right is None:
return False
elif left.val != right.val:
return False
else:
outside = self.compare(left.left, right.right)
inside = self.compare(left.right, right.left)
return outside and inside
类似的,可以写出层次遍历的解法:
from collections import deque
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
# 层次遍历法
if not root:
return True
queue = deque([root.left, root.right])
while queue:
size = len(queue)
if size % 2:
return False
level_values = []
for _ in range(size):
node = queue.popleft()
if node:
level_values.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
level_values.append(None)
if level_values != level_values[::-1]:
return False
return True
Leetcode 100 相同的树
题目描述
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入: p = [1,2,3], q = [1,2,3]
输出: true
解法
踩坑
这道题目在做的时候我踩了一个大坑,就是我以为只要保证二叉树的前序、中序、后序和层次遍历的结果相同,就可以保证他们完全相同,为此我写了这样一个代码:
from collections import deque
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
def pre_order_traversal(root):
if not root:
return []
left = pre_order_traversal(root.left)
right = pre_order_traversal(root.right)
return [root.val] + left + right
def mid_order_traversal(root):
if not root:
return []
left = mid_order_traversal(root.left)
right = mid_order_traversal(root.right)
return left + [root.val] + right
def post_order_traversal(root):
if not root:
return []
left = mid_order_traversal(root.left)
right = mid_order_traversal(root.right)
return left + right + [root.val]
def layer_traversal(root):
queue = deque()
result = []
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
items.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(items)
return result
p_pre, p_mid, p_post, p_layer = pre_order_traversal(p), mid_order_traversal(p), post_order_traversal(p), layer_traversal(p)
q_pre, q_mid, q_post, q_layer = pre_order_traversal(q), mid_order_traversal(q), post_order_traversal(q), layer_traversal(q)
return p_pre == q_pre and p_mid == q_mid and p_post == q_post and p_layer == q_layer
结果,总是通不过一个测试样例,即
p=[1,1],q=[1,null,1]
这时,我才想到,二叉树中的值是可能存在重复值的!,所以这道题目不能通过这种方式判断,起码要考虑None的情况才可以。于是,我进行了一步改进,即考虑节点不存在为None时,我也将其加入层次遍历值的列表中。这样,只使用层次遍历的结果就能判断是否相同。
from collections import deque
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
def layer_traversal(root):
queue = deque()
result = []
if root:
queue.append(root)
while queue:
items = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
if node:
items.append(node.val)
else:
items.append(None)
continue
if node.left:
queue.append(node.left)
else:
queue.append(None)
if node.right:
queue.append(node.right)
else:
queue.append(None)
result.append(items)
return result
p_layer = layer_traversal(p)
q_layer = layer_traversal(q)
return p_layer == q_layer
当然,这道题的简单解法或者更容易理解的版本应该使用递归,即判断每个节点的左右节点是否相同。
from collections import deque
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if p is None and q is None:
return True
elif p is None or q is None:
return False
elif p.val != q.val:
return False
else:
left = self.isSameTree(p.left, q.left)
right = self.isSameTree(p.right, q.right)
return left and right
Leetcode 572 另一棵树的子树
题目描述
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
示例 1:
输入: root = [3,4,5,1,2], subRoot = [4,1,2]
输出: true
解法
这道题目完全可以借助上面的判断两棵树完全相同的方法,使用递归来解决,即我们只要判断两棵树是否完全一致,或者小树是不是大树的左/右子树的子树。
结束的条件及大树或小树变为None。具体代码如下:
class Solution:
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
if root is None and subRoot is None:
return True
elif root is None or subRoot is None:
return False
else:
return self.isSameTree(root, subRoot) or self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if p is None and q is None:
return True
elif p is None or q is None:
return False
elif p.val != q.val:
return False
else:
left = self.isSameTree(p.left, q.left)
right = self.isSameTree(p.right, q.right)
return left and right