依次分别是二叉树的前序、中序、后序、层次遍历,除了层次遍历都有明确要求用循环而非递归做
144.前序遍历
直接给大家看discussion部分的优美代码吧,自己的就不拿出来丢人了…
class Solution(object):
def preorderTraversal(self, root):
ret = []
stack = [root]
while stack:
node = stack.pop()
if node:
ret.append(node.val)
stack.append(node.right)
stack.append(node.left)
return ret
很直观地(Empirically),我们使用栈
最后我们也会发现,由于栈具有后进先出的特点
在二叉树的循环遍历中,栈是实用度很高的
本题的注意细节就是:
节点从栈中弹出来后就立刻压入list,然后再压子节点到栈中
由于是先序遍历
存储栈的子节点的时候是先右再左
由于栈FILO
94.中序遍历
直接给大家看discussion部分的优美代码吧,自己的就不拿出来丢人了…
class Solution:
def inorderTraversal(self, root):
result, stack = [], [(root, False)]
while stack:
cur, visited = stack.pop()
if cur:
if visited:
result.append(cur.val)
else:
stack.append((cur.right, False))
stack.append((cur, True))
stack.append((cur.left, False))
return result
中序遍历跟前序比有点不一样的地方就是要考虑:
我们在将某个节点弹栈的时候,这个节点是否用完就可以丢弃(是否还需要再次入栈)
答案是显然的。虽然是中序遍历,我们在弹出该节点的时候可以将该节点的值压入返回的list中,但是我们还要将其存储在栈中用于保留其子节点返回的路径。
所以我们要进行标记以区分两次弹出(因为第二次弹出后该节点可被抛弃)
本题的注意细节就是:
弹栈的时候一定要先判断节点是否None(因为压栈的时候没有考虑)
存储栈的子节点的时候仍然是先右再左
由于栈FILO
145.后序遍历
后序遍历跟中序遍历的思想比较相似
class Solution:
def inorderTraversal(self, root):
result, stack = [], [(root, False)]
while stack:
cur, visited = stack.pop()
if cur:
if visited:
result.append(cur.val)
else:
stack.append((cur.right, False))
stack.append((cur, True))
stack.append((cur.left, False))
return result
大佬们还提出了另一种思路,就是在先序遍历的基础上进行修改:
(将先序的左右子节点压栈的顺序换下,然后最后reverse一下list就把先序变成后序了)
class Solution:
# @param {TreeNode} root
# @return {integer[]}
def postorderTraversal(self, root):
traversal, stack = [], [root]
while stack:
node = stack.pop()
if node:
# pre-order, right first
traversal.append(node.val)
stack.append(node.left)
stack.append(node.right)
# reverse result
return traversal[::-1]
- traversal[ : : -1]可以把返回列表反转后的copy (类似的,[::2]返回的就是list里面序号(从1开始计数)为偶数的元素
小总结:
这三个遍历都是以stack为主体,如果stack空了,那说明所有点遍历结束了,那么程序也可以结束了(所以外循环是while stack: )
102.层次遍历
def levelOrder(self, root):
ans, level = [], [root]
while root and level:
ans.append([node.val for node in level])
LRpair = [(node.left, node.right) for node in level]
level = [leaf for LR in LRpair for leaf in LR if leaf]
return ans
- 亮点:
1. 由于level需要存储的只是上一层level的东西,所以完全不需要层数这个参数,每次清空level即可
2. 将我丑陋的if判断改成了双重循环 - 列表生成式写嵌套循环:外层循环写在前面(这才符合”后面要用前面出现过的变量的“道理)
- 但是列表生成器似乎不支持else语法
- list.extend(list b) 相当于 list a + list b :在本列表内添加另一个列表的所有元素而非只添加一个元素(这个元素本身是个列表)