一、层序遍历 - 🔗
下面这张图表可以很清晰地展示层序遍历的过程。
层序遍历按照从上到下、从左到右的顺序访问每一层的节点。首先将根节点加入队列,然后在每一轮循环中,取出队列中的节点,访问其值,并将其左右子节点(如果存在)加入队列。用队列的size去记录每一层节点的个数。
102. 二叉树的层序遍历 - 🔗
💡这道题就是二叉树层序遍历的最基础的应用。需要注意的是,要在循环开始前先插入根节点,循环判断的结果就是que中是否有元素:如果还有元素,说明还有节点没有遍历完,需要继续遍历。此外,插入左右节点时,需要先判断左右节点是否为空if cur.left:
和if cur.right:
,这样可以避免在que中插入None
。这样不仅可以避免在下一次循环进过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
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
res = []
# 创建一个队列用于存放每一层的节点
que = deque()
# 先存入根节点root
if root:
que.append(root)
while que:
record = []
size = len(que)
while size > 0:
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
record.append(que.popleft().val)
size -= 1
res.append(record)
return res
107. 二叉树的层序遍历 II - 🔗
💡这道题跟上一道题唯一不同的地方就在于遍历输出的结果顺序。上一题就是按照遍历的顺序输出,而这题则是逆序,所以只需在最后return的时候将list反转即可。
# 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
from collections import deque
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
res = []
que = deque()
if root:
que.append(root)
while que:
record = []
size = len(que)
while size:
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
record.append(que.popleft().val)
size -= 1
res.append(record)
return res[::-1]
199. 二叉树的右视图 - 🔗
💡这道题也不复杂。想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。可以很快发现,我们需要得到的结果其实就是每一层的最后一个节点。那么只需要在res.append(que.popleft().val)
前加入判断条件,去判断当前节点是否是最后一个节点即可。
# 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
from collections import deque
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
# 记录每一层的最右节点,也就是每一层的最后一个节点,即size = 1
res = []
que = deque()
if root:
que.append(root)
while que:
size = len(que)
while size:
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 如果遇到了每一层的最后一个元素,则append到res后面
if size == 1:
res.append(que.popleft().val)
else:
que.popleft()
size -= 1
return res
637. 二叉树的层平均值 - 🔗
💡只需将插入的结果从每层所有元素,改为整层所有元素sum值与元素个数相除得到的结果即可。
# 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
from collections import deque
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
res = []
que = deque([root])
while que:
# 记录每一层的总和
sum = 0
size = len(que)
for i in range(size):
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
sum += que.popleft().val
res.append(sum / size)
return res
429. N 叉树的层序遍历 - 🔗
💡这道题需要注意的是,Node不再具有left和right指针,而是有一个children,而children是list类型的,因为一个节点可以有N个字节点。所以原先插入子节点的那一步,需要改为遍历children里的每一个元素并append到level_record中。
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
from collections import deque
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
res = []
que = deque()
if root:
que.append(root)
while que:
level_record = []
size = len(que)
for i in range(size):
cur = que[0]
# children是一个列表
for child in cur.children:
que.append(child)
level_record.append(que.popleft().val)
res.append(level_record)
return res
515. 在每个树行中找最大值 - 🔗
💡这道题也比较清晰,只需要定义一个变量去记录每一层的最大值即可。注意再给这个变量初始化的以后可以将它设为负无穷,即max_rec = float('-inf')
。这样当max_rec在遇到每一层的第一个元素的时候,最大值一定会改变。此外,max_rec = tmp if tmp > max_rec else max_rec
这条语句也可以直接改写成max_rec = max(tmp, max_rec)
# 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
from collections import deque
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
res = []
que = deque()
if root:
que.append(root)
while que:
size = len(que)
max_rec = float('-inf')
for i in range(size):
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
tmp = que.popleft().val
max_rec = tmp if tmp > max_rec else max_rec
res.append(max_rec)
return res
116. 填充每个节点的下一个右侧节点指针 - 🔗
💡这道题看上去挺复杂的,但是仔细想想没有那么难。最难的部分就是怎么将左子树的最右节点的next指针指向右子树的最左节点。但是当用层序遍历去遍历每一层的元素,就可以将每一层的元素从左到右按顺序放进队列中。那么就可以直接让当前元素的next指向下一元素即可。
但是需要注意的是,因为在循环过程中会在最后插入当前节点的子节点,所以当遇到每一层的最后一个元素的时候,需要手动判断一下,不执行改变next值的操作(next默认为None
)。
"""
# 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
"""
# 当遇到next为None的时候,说明这一层结束了,输出'#',返回结果是root
from collections import deque
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
que = deque()
if root:
que.append(root)
while que:
size = len(que)
for i in range(size):
# cur从每一层的最左侧元素开始遍历
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
if i != size - 1:
cur.next = que[1]
que.popleft()
return root
117. 填充每个节点的下一个右侧节点指针 II- 🔗
💡跟上题思路和代码一模一样。
"""
# 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
"""
# 当遇到next为None的时候,说明这一层结束了,输出'#',返回结果是root
from collections import deque
class Solution:
def connect(self, root: 'Node') -> 'Node':
que = deque()
if root:
que.append(root)
while que:
size = len(que)
for i in range(size):
# cur从每一层的最左侧元素开始遍历
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
if i != size - 1:
cur.next = que[1]
que.popleft()
return root
104. 二叉树的最大深度 - 🔗
💡最大深度也就代表需要遍历到二叉树的最后一个元素,那么最后一个元素所在的层数就是二叉树的最大深度。
# 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
# max_length记录一共有几层
from collections import deque
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
max_depth = 0
que = deque()
if root:
que.append(root)
while que:
size = len(que)
max_depth += 1
for i in range(size):
cur = que[0]
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
que.popleft()
return max_depth
111. 二叉树的最小深度- 🔗
💡跟上题不一样的是,二叉树的最小深度只需要找到第一个叶子节点节点即可。判断是否是叶子节点只需要判断当前节点的左右子节点是否同时为None
,即if not cur.left and not cur.right: return min_depth
# 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
# 当某一个节点既没有左节点也没有右节点时,即cur.left == None and cur.right == None,说明当前节点为叶子节点
from collections import deque
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
min_depth = 0
que = deque()
if root:
que.append(root)
while que:
size = len(que)
min_depth += 1
for i in range(size):
cur = que[0]
if not cur.left and not cur.right:
return min_depth
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
que.popleft()
return min_depth
二、226.翻转二叉树 - 🔗
讲解-🔗
💡这道题比较好想的思路是层序遍历,也可能是之前一直在做层序,惯性思维代入了?
方法一:层序遍历
from collections import deque
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
que = deque()
if root:
que.append(root)
while que:
size = len(que)
for _ in range(size):
cur = que[0]
cur.left, cur.right = cur.right, cur.left
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
que.popleft()
return root
方法二:递归 - 前序遍历
对递归不太熟悉,感觉并不好写,但是在做题时可以通过以下几个方面思考是否可以用递归:
- 问题是否可以被划分为相同的子问题:
递归常常用于解决能够被划分为相似或相同子问题的问题。如果问题的解决方案可以通过解决一个问题的子问题来构建,那么递归可能是一个好的选择。- 问题是否可以被简化:
递归通常在解决问题时不断简化问题的规模。每次递归调用都将问题缩小为一个规模更小的子问题,直到达到基本情况,即问题足够小,可以直接解决。- 问题是否有重叠子问题:
如果问题的解决方案涉及到许多相同的子问题,递归就可以通过记忆化或动态规划等方法避免重复计算,提高效率。- 问题是否可以通过递归函数来描述:
问题的解决方案是否可以通过递归函数的定义来自然而然地表达。递归函数通常描述问题的解决方法与其子问题的解决方法之间的关系。- 问题是否有明显的递归出口:
递归函数需要有一个或多个出口条件,即当问题规模足够小时,可以直接返回结果,而不再进行递归。
在确定这道题可以用递归之后,明确递归算法的三个要素:
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root:
return root
root.left, root.right = root.right, root.left
root.left = self.invertTree(root.left)
root.right = self.invertTree(root.right)
return root
三、101.对称二叉树 - 🔗
讲解-🔗
💡这道题刚开始享用层序遍历,后面遇到一个特殊情况就报错了。递归比较难想。
# 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 isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
return self.compare(root.left, root.right)
def compare(self, left, right):
#首先排除空节点的情况
if left == None and right != None: return False
elif left != None and right == None: return False
elif left == None and right == None: return True
#排除了空节点,再排除数值不相同的情况
elif left.val != right.val: return False
#此时就是:左右节点都不为空,且数值相同的情况
#此时才做递归,做下一层的判断
outside = self.compare(left.left, right.right) #左子树:左、 右子树:右
inside = self.compare(left.right, right.left) #左子树:右、 右子树:左
isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理)
return isSame