前序
## 递归实现
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
中序
## 递归实现
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
retrn self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
后序
## 递归实现
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
自己想的。前序简单,中序和后序时由于要先遍历其左右孩子,因此节点要重新入栈,所以额外加一个visit判断弹出是待处理还是处理完,处理完则直接加入到遍历序列ans中,待处理则进行处理并更新状态重新入栈。[力扣评论区看到,也可以不额外设置set记录状态,可以在处理完入栈的时候再入一个None标记,表示下一个结点是已经处理完可以直接添加到遍历序列中的]
前序
## 栈实现一
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
stack = [root]
ans = []
while stack:
node = stack.pop()
ans.append(node.val)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return ans
中序
## 栈实现[自己想的]
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
stack = [root]
visitset = set()
ans = []
while stack:
node = stack.pop()
if node in visitset:
ans.append(node.val)
continue
if node.right:
stack.append(node.right)
stack.append(node)
visitset.add(node)
if node.left:
stack.append(node.left)
return ans
后序
## 栈实现一
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
stack = [root]
ans = []
visitSet = set() ## 或用空节点标记
while stack:
node = stack.pop()
if node in visitSet:
ans.append(node.val)
continue
stack.append(node)
visitSet.add(node)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return ans
力扣看到的,真正用栈模拟递归的过程。总结来说就是一路找左孩子,然后用栈回溯。 前序和中序都好实现,后序要复杂一些。
前序: 在一路找左孩子途中就将节点添加到遍历序列中
## 栈实现二
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans = []
stack = []
node = root
while stack or node:
while node:
ans.append(node.val)
stack.append(node)
node = node.left
pnode = stack.pop()
node = pnode.right
中序: 在弹栈元素时加入到遍历序列,因为弹栈说明其左孩子已经访问完。
## 栈实现[大佬的],实际在模拟递归
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
node = root
stack = []
ans = []
while stack or node:
while node:
stack.append(node)
node = node.left
pnode = stack.pop()
ans.append(pnode.val)
node = pnode.right
return ans
后序: 后序因为不知道弹出的节点是否访问完了右子树,因此用一个前驱节点prev维护上一次最后加入ans的[最后访问的]节点,若该节点的右节点为空或者为prev.说明该节点左子树和右子树皆访问完,可以加入ans中,并更新prev
## 栈实现三:真正的后续遍历
## 因为不知道弹出的节点是否访问完了右子树,因此用一个前驱节点prev维护上一次最后加入ans的[最后访问的]节点
## 若该节点的右节点为空或者为prev.说明该节点左子树和右子树皆访问完,可以加入ans中,并更新prev
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack = []
ans = []
node = root
prev = None
while stack or node:
while node:
stack.append(node)
node = node.left
pnode = stack.pop()
if not pnode.right or pnode.right == prev:
ans.append(pnode.val)
prev = pnode
node = None
else:
stack.append(pnode)
node = pnode.right
return ans
后序还有一奇淫技巧是把他看成 变相前序 中-右-左 的逆向输出,这样在稍改前序[改左右子树入栈顺序]然后把遍历序列求一个逆转就行。
但是这种方法只是用于拓宽一下思路而已。真正来说没有什么意义,因为他并不是在以后序的顺序去访问数据,而“遍历”的本质是对内存的有序访问。
力扣评论区的Edward Elric大神:在做迭代版本之前,我建议大家先问问各类“遍历”算法的本质是什么?是最后输出的那一串有序的数字吗?数字的顺序是对的,遍历算法就是对的吗?个人认为,以上问题的答案都应该是:否。“遍历”的本质是对内存的有序访问,失去了访问顺序,即便你用各种数据结构恢复了这个次序,遍历本身也显得毫无意义。常见的后序遍历写法中有一种已经出现在评论区了——它的思想也很简单,大家做过单词串翻转吗?
## 栈实现二[投机取巧]:直接实现不方便,实际上就是变相前序 中-右-左 的逆向输出
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack = []
ans = []
node = root
while stack or node:
while node:
ans.append(node.val)
stack.append(node)
node = node.right
pnode = stack.pop()
node = pnode.left
return ans[::-1]