帕先生之二叉树非递归遍历下篇

32 阅读3分钟

讲完了二叉树前序遍历和后序遍历的非递归代码, 那么这期我们来讲一下中序遍历的非递归实现

1、中序遍历

左中右

binarytree.png

对于一棵树而言, 我们都是先遍历根节点, 然后依次遍历左右孩子

对于前序和后序遍历而言, 我们都是先处理根节点, 我们发现 处理和遍历是在一起的

但是对于中序遍历来说, 我们需要先遍历根节点 10, 然后遍历左子树 5-> 4 -> 6, 然后 4 才是我们的处理节点, 然后再处理 5 , 再处理 6 , 最后再返回处理 根节点 10

模拟入栈过程

因为最后结果是左中右, 所以我们要把最左边的节点弹出 存入Res里, 这里会从根节点开始, 一直遍历左孩子, 直到遇到叶子节点, 即把 10 , 5 , 4 先后入栈 image.png

遵循以下原则:

遇到一个节点, 只要左不为空, 就一直遍历他的左孩子, 同时入栈, 为空, 则从栈顶弹出元素, 假设是A, 再遍历A的右孩子, 空则继续弹出元素, 非空则再遍历右孩子的左节点......

此时我们发现节点 4 的左孩子为空, 则弹出 4 , 同时加入Res

image.png

发现 4 的右孩子为空, 则继续弹出元素, 把 5 加入 Res

image.png

发现 5 的 右孩子 6 不为空, 则 把 6 入栈

image.png

发现 6 的左孩子为空, 则弹出 6, 同时加入Res

image.png

6 的 右孩子为空, 继续弹出元素, 把 10 加入Res

image.png

发现 10 的右孩子不为空, 则把右孩子 20 入栈

image.png

遇到一个节点, 优先检查他的左孩子是否有值, 发现不为空, 则把 2 入栈

image.png

2 的左孩子为空, 则弹出元素, 同时加入到Res

image.png

2 的右孩子为空, 则继续弹出, 把 20 加入到Res

image.png

发现 20 的右孩子非空, 把 8 入栈

image.png

8 的左孩子为空, 弹出元素, 加入 Res

image.png

8 的右孩子也为空, 但此时栈已空, 无法弹出元素, 遍历结束

得到 4, 5, 6, 10, 2, 20, 8

遇到一个节点, 优先找他的左孩子

  • 非空, 入栈, 继续找他的左孩子
  • 空, 从栈顶弹出元素

弹出元素后, 找他的右孩子

  • 非空, 遇到一个节点, 此时应该干嘛?(重复一开始的步骤, 找他的左孩子)
  • 空, 弹出元素

栈为空, 结束遍历

2、代码实现

叶子节点本身也是棵树, 只不过左为空, 右为空, 所以对于叶子节点来说, 也遵循上述原则, 先找左, 叶子节点的左孩子为空, 所以栈顶弹出元素(叶子节点), 然后找叶子节点的右孩子, 也为空, 则继续弹出(其实此时弹出的已经是叶子节点的父节点了), 找叶子节点的父节点的右孩子, 以此反复...

# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res, stack = [], []
        # 从根节点开始查找
        while stack or root:
            if root: # 遇到一个节点就先找left
                stack.append(root)
                root = root.left    # 优先找left, 只要left有值, 就会入栈
            else: 
                temp = stack.pop() # left 为空, 则弹出
                res.append(temp.val) # 记录
                root = temp.right # 找右孩子
        return res

至此, 二叉树前中后序的递归版本和迭代版本已经讲解完毕, 接下来会讲层次遍历