层序遍历(102)
BFS 在树中的体现。与 DFS 不同的是,优先广度的搜索需要层级顺序,从而需要队列来进行实现。
思路:将每一层的节点都保存在队列中,通过额外的变量 size 来记录当前层中的节点数量(在每次对同一层的节点开始遍历前记录)。
迭代
class Solution:
from collections import deque
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None:
return []
results = []
queue = deque()
queue.append(root)
while (len(queue) > 0):
size = len(queue)
temp_lst = []
# it can be len(queue), although queue is dynamic in this while loop
# when the loop is called first time, len(queue) is fixed
# but this format will be confusing
for i in range(size):
curr_node = queue.popleft()
temp_lst.append(curr_node.val)
if curr_node.left != None:
queue.append(curr_node.left)
if curr_node.right != None:
queue.append(curr_node.right)
results.append(temp_lst)
return results
递归
递归的思路完成层次遍历还是像其他的递归一样轻松,但注意这里多了一个不容易想到的变量 level,主要是记录当前到达的层数。这不属于递归终止条件,而是题目中的结构要求,即用 level 标记是否是第一次到达该层,如果是则添加新的 []
来储存该层的节点值。
主要是递归思路的锻炼。
class Solution:
def levelRec(self, root: Optional[TreeNode], results: List[List[int]], level: int) -> None:
if root == None:
return results
if len(results) == level: # first time reach this new level
results.append([])
results[level].append(root.val)
self.levelRec(root.left, results, level + 1)
self.levelRec(root.right, results, level + 1)
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
results = []
self.levelRec(root, results, 0)
return results
226. 翻转二叉树
递归的思路是较为简单的,在单层遍历中,交换当前节点的左右子节点即可。对于左右节点的递归必须连在一起进行,所以前序和后序遍历都是可以的。而中序遍历会很 tricky,因为只翻转一个子节点之后进行节点的更新,可能会再次翻转相同的节点(中序可以解,但思路上会很麻烦)。
重要的是,要意识到自己要使用什么顺序的遍历。这里面涉及两点:
- 自己当前的思路是什么样的遍历顺序?
- 为什么要使用这样的遍历顺序?
不同的遍历顺序代表了对获取信息的顺序要求,所以实际上是根据题目中的信息顺序来决定遍历顺序。
前序遍历
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None:
return
temp_left = self.invertTree(root.left)
temp_right = self.invertTree(root.right)
root.left, root.right = temp_right, temp_left
return root
层次遍历
BFS 的层次遍历同样可以解决,对每层的每个节点进行左右子节点的翻转即可。
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None:
return None
stack = [root]
while (len(stack) > 0):
size = len(stack)
for i in range(size):
curr_node = stack.pop()
curr_node.left, curr_node.right = curr_node.right, curr_node.left
if curr_node.left != None:
stack.append(curr_node.left)
if curr_node.right != None:
stack.append(curr_node.right)
return root
101. 对称二叉树
读懂题目的目标非常重要。这题和上一题最大的不同在于,递归中的单层逻辑不能仅依靠当前节点的子节点(例如,比较子节点是否相等),还需要子节点的子节点。
这里要比较的是外侧与外侧节点,内侧与内侧节点,所以对于左右子树来说,遍历(比较)顺序应该是相反的,即一个子树是“左右中”,一个子树是“右左中”。
此题的重点思路是,只有完成了子节点(的子节点)的比较,才能得知当前节点的信息,从而决定是向上传递还是可以直接返回。这是一种广义上的后序遍历,即“当前节点的信息依赖于子节点的信息”。分析出广义后序是这道题的递归方向最大的重点!
另外一点是要清晰地分析当前递归的终止条件(此题比较复杂),递归的参数(同时从左右子树出发,所以需要左节点和右节点,而非单一的当前节点)。
后序遍历
class Solution:
def compare(self, left_node: Optional[TreeNode], right_node: Optional[TreeNode]) -> bool:
if left_node == None and right_node == None:
return True
elif left_node != None and right_node == None:
return False
elif left_node == None and right_node != None:
return False
elif left_node.val != right_node.val:
return False
else:
outside = self.compare(left_node.left, right_node.right) # left, right
inside = self.compare(left_node.right, right_node.left) # right, left
return outside and inside # middle
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if root == None:
return True
return self.compare(root.left, root.right)
层次遍历
较为直接的方法,直接遍历每一层并检查当前层是否回文。
class Solution:
from collections import deque
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if root == None:
return True
queue = deque([root])
while (len(queue) > 0):
size = len(queue)
curr_lst = []
for i in range(size):
curr_node = queue.popleft()
if curr_node != None:
curr_lst.append(curr_node.val)
queue.append(curr_node.left)
queue.append(curr_node.right)
else:
curr_lst.append(None)
if curr_lst != curr_lst[::-1]:
return False
return True