二叉树的前/中/后序遍历,递归+栈迭代实现[同门说今天字节刚面迭代版]

递归版本很简单就直接贴下代码了:

前序

## 递归实现
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]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值