树的迭代遍历(前、中、后)python

本文详细介绍了如何使用迭代方式实现树的前序、中序和后序遍历,以及空间复杂度为O(1)的Morris遍历方法,通过辅助栈和节点状态管理展示了不同的遍历策略。
摘要由CSDN通过智能技术生成

递归实现树的遍历十分简单 本篇文章总结 树的迭代遍历

前序遍历

辅助栈: 先压右 再压左 所以 先出左 再出右 很好理解 配合代码如下

if not root: return []

stack = [root]
res = []
    

while stack:
    # 除了根节点 根据之前压栈的规则 如果某个节点存在左子节点 那么该左子节点一定首先出栈
    # 左子节点出栈之后 如果该节点存在右子节点 右子节点一定紧随出栈 因为他们是紧密连续入栈的
    # 这样就能做到左边的子树始终在栈尾 首先被pop出来 
    # 起初被压栈的右子树的根节点始终被压制 直到左子树的所有元素被遍历之后 才开始遍历右子树
    # 上面一行话对理解迭代做法非常重要,在任何节点 左子树的元素都比右子树的元素更先遍历
    # 因为 右子树的根节点始终被左子树的根节点压着
    node = stack.pop()
    res.append(node.val)
    # 先将右节点入栈 再将左节点入栈
    if node.right: stack.append(node.right)
    if node.left: stack.append(node.left)

print(res)

也可以这样写:

if not root: return []

stack = [root]
node = root
while stack or node:
    # 从根节点开始 向左边遍历 并将遍历的所有节点压栈
    while node:
        res.append(node.val)
        stack.append(node)
        node = node.left
    # 直到遇到最左边的节点node
    node = stack.pop()
    # 最左边的节点node的右节点继续上述操作 如果最左边的节点是叶节点
    # 那么 之后的while node: 直接跳过 更新node = stack.pop()为第二左边的节点
    # 对第二左边的节点的右节点继续上述操作, 同样的如果第二左边的节点没有右节点
    # 继续更新 node为第三左边的节点 直到将node更新为根节点root
    # 此时stack为空,但是node不为空
    # 再将node更新为root.right 如果root.right为空 那么循环结束
    # 如果root.right不为空while node:不能跳过 即此时我们对根节点的右子树继续了我们
    # 之前的所有操作
    node = node.right
print(res)

这样写的思想是,保留了子节点与父节点之间的联系(通过node = stack.pop()),当一直往左遍历到头了之后,回到父节点对父节点的右子树进行同样的,从父节点的右节点开始一直往左遍历。

可以思考一下这个代码和之前的代码有什么不同。

中序遍历,代码:

if not root: return []


stack = []
node = root

while stack or node:
    # 一直往左找 但是 在这个过程中暂时先不管节点的值 而是先将这些节点压栈
    while node:
        stack.append(node)
        node = node.left
    # 找到最左边的节点之后,将第一个结果放入到res中
    node = stack.pop()
    res.append(node.val)
    # 如果最左边的节点有右节点 那么继续对右节点进行上述操作
    # 如果最左边的节点是叶节点,那么之后node = stack.pop()就是该节点的父节点
    # 继续对该父节点判断 如果该父节点 有右节点 那么继续对右节点进行上述操作
    node = node.right

print(res)

中序遍历的核心是 先将根节点和根节点往左一直遍历的节点压栈(第一次压栈),出栈一个节点,记录该节点的值,再将该节点的右节点和该右节点往左一直遍历的节点压栈(如果右节点不为空,第二次压栈),这样就能实现中序遍历。

后序遍历:

代码:

if not root: return []

stack = [root]
node = root
while stack or node:
    # 从根节点开始 向右边遍历 并将遍历的所有节点压栈
    while node:
        res.append(node.val)
        stack.append(node)
        node = node.right
    # 直到遇到最右边的节点node
    node = stack.pop()
    # 最右边的节点node的左节点继续上述操作 如果最右边的节点是叶节点
    # 那么 之后的while node: 直接跳过 更新node = stack.pop()为第二右边的节点
    # 对第二右边的节点的左节点继续上述操作, 同样的如果第二右边的节点没有左节点
    # 继续更新 node为第三右边的节点 直到将node更新为根节点root
    # 此时stack为空,但是node不为空
    # 再将node更新为root.left如果root.left为空 那么循环结束
    # 如果root.left不为空while node:不能跳过 即此时我们对根节点的左子树继续了我们
    # 之前的所有操作
    node = node.left
# 最后将结果倒序返回即可
print(res[::-1])

这是比较取巧的方法,模拟栈的另外一个方法是:

if not root: return []
stack, res = [(0,root)], []

while stack:
    flag, node = stack.pop()
    if not node: continue
    if flag == 1:
        res.append(node.val)
    else:
        stack.append((1,node))
        stack.append((0,node.right))
        stack.append((0,node.left))

print(res)

在入栈时加入标识,当标识为1的时候才将该节点的值放入结果中,如果不唯一,就将该节点重新压栈标识改为1并且将左右子节点也压栈。

也可以这样写:

    
res = []
stack = []
prev = None

while root or stack:
    while root:
        stack.append(root)
        root = root.left
    root = stack.pop()
    if not root.right or root.right == prev:
        res.append(root.val)
        prev = root
        root = None
    else:
        stack.append(root)
        root = root.right

return res

注意这三种迭代写法可以总结出一个模板,只要观察出节点值的打印顺序导致的while循环体改变的规律即可快速记忆,可以自己想想~

Morris遍历已经更新:

空间复杂度为O(1)的Morris遍历代码(python)-CSDN博客

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值