题目链接 102. 二叉树的层序遍历 - 力扣(Leetcode)
记录两种写法,一种是作者看错题目要求后,在错误代码上硬着头皮改出来的代码,(记录在博客中随时鞭打自己)。一种是根据优秀题解理解后重新编写的代码
这道题我看了许多题解,没有找到时间与空间都优秀的解法,基本都是时间与空间要牺牲一个
一. 作者的一坨**代码(很多冗余的条件判断)
如果读者想见识作者当时的愚蠢想法,可以当乐子看一看,虽然也能100%通过,但是实在差。
严重缺点:flag的判断太过于鸡肋,万一题目没给你结点值范围直接原地爆炸
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
# 根节点为空返回[]
res = [0] # 0 完全作占位置作用,作者为了后面得索引好对齐才放的。输出时不带上
yes, no = 1, 0 # 标志是否写入res了,因为它可能有子结点且遍历顺序较后,需要将其暂存于栈
stack = [(1, no, root)]
# (深度,是否进res了,结点)
while stack:
dep, flag, node = stack.pop()
dep_now = dep + 1 # 子结点的深度
right_val, left_val = 10000, 10000 # 题目给出的结点值范围
# 原本想用False 但是万一结点值为0 ,后续判断会产生错误
son_through = [] # 父结点的子结点的遍历
if not flag: res.append([node.val]) # 这个flag完全只作用于root,作者当时脑子瓦特写的太复杂
# 因为明明检查到有结点当时就写入了
if node.right:
right_val = node.right.val
stack.append((dep_now, yes, node.right))
if node.left:
left_val = node.left.val
stack.append((dep_now, yes, node.left))
# 进栈顺序应为右结点先
# 但是进res顺序应该为左结点先
if -1000 <= left_val <= 1000: son_through.append(left_val)
if -1000 <= right_val <= 1000: son_through.append(right_val)
if len(res) > dep_now:
res[dep_now].extend(son_through)
# 作者当时只合并了同父结点的结点值,并没有合并同层次结点
# 深度的作用就在这里,res中每一个[]中的值都是同深度的也就是同层次的
# 按照上面的循环处理方式,左结点C的子结点可能比 同层次结点(与C)更早进入res,但是不用担心,不同层次在res中不在一个子列表中
# 比如深度为3(深度为2时右结点的子结点)的右子树结点,在res中应该加入的位置就是res[3](前面res[0]是0,所以索引对齐了)
# 当遍历到右子树的深度为2的结点时判断res长度长于本身深度,就代表着深度>2的结点已经进res了
# 那么合并后的同父结点值就不能直接插入到res的尾端而是应该合并到前面同深度的子列表中去
elif son_through:
res.append(son_through)
# 该深度第一次出现,直接插入到res尾端
return res[1:]
# 不带上开头的0
二 代码改进
找遍了题解,没有找到这个层序遍历的运行结果时间与空间兼得的。要么时间短了空间占用多,要么空间占用少了时间长了,总有一个是红色的。
下面这个方法代码的复杂程度较低,简单易懂
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
res = []
queue = deque([root])
# 队列,创建时加入root
while queue:
level_through = []
# 用来存放这个层次的遍历结果
for _ in range(len(queue)):
# 提一句:本次循环结束前就算len(queue)变化,用的也是一开始的值。
# 所以不用担心过程中入队列会影响结果。
# 每次for循环的对象都是一个层次(深度)上的所有结点,
# 这些结点的子结点(就是下一深度的结点)按照从左到右入队列
node = queue.popleft()
level_through.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(level_through)
return res