二叉树(前序,中序,后序,层序)遍历递归与循环的python实现

本文详细介绍了二叉树的前序、中序、后序及层序遍历的递归与循环实现方法,包括每种遍历方式的打印顺序、实现原理及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

binary-tree.png

二叉树的遍历是在面试使比较常见的项目了。对于二叉树的前中后层序遍历,每种遍历都可以递归和循环两种实现方法,且每种遍历的递归实现都比循环实现要简洁。下面做一个小结。

一、中序遍历

前中后序三种遍历方法对于左右结点的遍历顺序都是一样的(先左后右),唯一不同的就是根节点的出现位置。对于中序遍历来说,根结点的遍历位置在中间。

所以中序遍历的顺序:左中右

1.1 递归实现

每次递归,只需要判断结点是不是None,否则按照左中右的顺序打印出结点value值。

class Solution:
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return [] 
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
1.2 循环实现

循环比递归要复杂得多,因为你得在一个函数中遍历到所有结点。但是有句话很重要:

对于树的遍历,循环操作基本上要用到栈(stack)这个结构

对于中序遍历的循环实现,每次将当前结点(curr)的左子结点push到栈中,直到当前结点(curr)为None。这时,pop出栈顶的第一个元素,设其为当前结点,并输出该结点的value值,且开始遍历该结点的右子树。

thisfile.png

例如,对于上图的一个二叉树,其循环遍历过程如下表:

No.输出列表sol栈stack当前结点curr
1[][]1
2[][1]2
3[][1,2]4
4[][1,2,4]None
5[4][1,2]4 -> None(4的右结点)
6[4,2][1]2 -> 5
7[4,2][1,5]None(5的左结点)
8[4,2,5][1]5 -> None(5的右结点)
9[4,2,5,1][]3
10[4,2,5,1][3]None
11[4,2,5,1,3][]None

可见,规律为:当前结点curr不为None时,每一次循环将当前结点curr入栈;当前结点curr为None时,则出栈一个结点,且打印出栈结点的value值。整个循环在stack和curr皆为None的时候结束。

class Solution:
    def inorderTraversal(self, root):
        stack = []
        sol = []
        curr = root
        while stack or curr:
            if curr:
                stack.append(curr)
                curr = curr.left
            else:
                curr = stack.pop()
                sol.append(curr.val)
                curr = curr.right
        return sol

二、前序遍历和后序遍历

按照上面的说法,前序遍历指根结点在最前面输出,所以前序遍历的顺序是:中左右

后序遍历指根结点在最后面输出,所以后序遍历的顺序是:左右中

2.1 递归实现

递归实现与中序遍历几乎完全一样,改变一下打印的顺序即可:

class Solution:
    def preorderTraversal(self, root):  ##前序遍历
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return [] 
        return  [root.val] + self.inorderTraversal(root.left) + self.inorderTraversal(root.right)
        
    def postorderTraversal(self, root):  ##后序遍历
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return [] 
        return  self.inorderTraversal(root.left) + self.inorderTraversal(root.right) + [root.val]

改动的地方只有return时函数的打印顺序。

2.2 循环实现

为什么把前序遍历和后序遍历放在一起呢?Leetcode上前序遍历是medium难度,后序遍历可是hard难度呢!

实际上,后序遍历不就是前序遍历的“反过程”嘛!

先看前序遍历。我们仍然使用栈stack,由于前序遍历的顺序是中左右,所以我们每次先打印当前结点curr,并将右子结点push到栈中,然后将左子结点设为当前结点。入栈和出栈条件(当前结点curr不为None时,每一次循环将当前结点curr入栈;当前结点curr为None时,则出栈一个结点)以及循环结束条件(整个循环在stack和curr皆为None的时候结束)与中序遍历一模一样。

再看后序遍历。由于后序遍历的顺序是左右中,我们把它反过来,则遍历顺序变成中左右,是不是跟前序遍历只有左右结点的差异了呢?然而左右的差异仅仅就是.left和.right的差异,在代码上只有机械的差别。

我们来看代码:

class Solution:
    def preorderTraversal(self, root):  ## 前序遍历
        stack = []
        sol = []
        curr = root
        while stack or curr:
            if curr:
                sol.append(curr.val)
                stack.append(curr.right)
                curr = curr.left
            else:
                curr = stack.pop()
        return sol
        
    def postorderTraversal(self, root): ## 后序遍历
        stack = []
        sol = []
        curr = root
        while stack or curr:
            if curr:
                sol.append(curr.val)
                stack.append(curr.left)
                curr = curr.right
            else:
                curr = stack.pop()
        return sol[::-1]

代码的主体部分基本就是.right和.left交换了顺序,且后序遍历在最后输出的时候进行了反向(因为要从中右左变为左右中

三、层序遍历

层序遍历也可以叫做宽度优先遍历:先访问树的第一层结点,再访问树的第二层结点...然后一直访问到最下面一层结点。在同一层结点中,以从左到右的顺序依次访问。

3.1 递归实现

递归函数需要有一个参数level,该参数表示当前结点的层数。遍历的结果返回到一个二维列表sol=[[]]中,sol中的每一个子列表保存了对应index层的从左到右的所有结点value值。

class Solution:
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        def helper(node, level):
            if not node:
                return
            else:
                sol[level-1].append(node.val)
                if len(sol) == level:  # 遍历到新层时,只有最左边的结点使得等式成立
                    sol.append([])
                helper(node.left, level+1)
                helper(node.right, level+1)
        sol = [[]]
        helper(root, 1)
        return sol[:-1]

PS:

Q:如果仍然按层遍历,但是每层从右往左遍历怎么办呢?

A:将上面的代码left和right互换即可

Q:如果仍然按层遍历,但是我要第一层从左往右,第二层从右往左,第三从左往右...这种zigzag遍历方式如何实现?

A:将sol[level-1].append(node.val)进行一个层数奇偶的判断,一个用append(),一个用insert(0,)

    if level%2==1:
        sol[level-1].append(node.val)
    else:
        sol[level-1].insert(0, node.val)
3.2 循环实现

这里的循环实现不能用栈了,得用队列queue。因为每一层都需要从左往右打印,而每打印一个结点都会在队列中依次添加其左右两个子结点,每一层的顺序都是一样的,故必须采用先进先出的数据结构。

以下代码的打印结果为一个一维列表,没有采用二维列表的形式。

class Solution:
    def levelOrder(self, root):
        if not root:
            return []
        sol = []
        curr = root
        queue = [curr]
        while queue:
            curr = queue.pop(0)
            sol.append(curr.val)
            if curr.left:
                queue.append(curr.left)
            if curr.right:
                queue.append(curr.right)
        return sol

其实,如果需要打印成zigzag形式(相邻层打印顺序相反),则可以采用栈stack数据结构,正好符合先进后出的形式。不过在代码上还要进行其他改动。

转载于:https://www.cnblogs.com/bjwu/p/9284534.html

### 二叉树前序、中后序遍历的例题练习 以下是一些关于二叉树前序、中后序遍历的经典例题和练习,供学习参考。 #### 练习题 1:已知前序和中序遍历,求后序遍历 已知一棵二叉树前序遍历为 `ABDHIEJCFKG`,中序遍历为 `HDIBEJAFKCG`,请写出该二叉树后序遍历结果。 - **解答**: 根据前序遍历的第一个节点为根节点的特性,可以确定根节点为 `A`。在中序遍历中,`A` 左边的部分为左子树,右边的部分为右子树。通过递归分析,最终得到后序遍历的结果为 `HIDJEBKFGCA`[^2]。 ```python # 示例代码实现后序遍历 class TreeNode: def __init__(self, data): self.data = data self.left = None self.right = None def build_tree(preorder, inorder): if not preorder or not inorder: return None root_val = preorder[0] root = TreeNode(root_val) root_index = inorder.index(root_val) root.left = build_tree(preorder[1:root_index + 1], inorder[:root_index]) root.right = build_tree(preorder[root_index + 1:], inorder[root_index + 1:]) return root def postorder_traversal(root): if root is None: return [] return postorder_traversal(root.left) + postorder_traversal(root.right) + [root.data] # 输入示例 preorder = ['A', 'B', 'D', 'H', 'I', 'E', 'J', 'C', 'F', 'K', 'G'] inorder = ['H', 'D', 'I', 'B', 'E', 'J', 'A', 'F', 'K', 'C', 'G'] root = build_tree(preorder, inorder) postorder_result = postorder_traversal(root) print("后序遍历结果:", postorder_result) ``` --- #### 练习题 2:根据先和中序遍历还原二叉树 已知一棵二叉树的先序遍历为 `EFHIGJK`,中序遍历为 `HFIEJKG`,请还原该二叉树并写出其后序遍历结果。 - **解答**: 通过先序遍历的第一个节点 `E` 确定根节点,在中序遍历中找到 `E` 的位置,分割左右子树。递归构建二叉树后,得到后序遍历结果为 `HIFJKEG`[^3]。 ```python # 示例代码实现和中还原二叉树 def build_tree_from_pre_in(preorder, inorder): if not preorder or not inorder: return None root_val = preorder[0] root = TreeNode(root_val) root_index = inorder.index(root_val) root.left = build_tree_from_pre_in(preorder[1:root_index + 1], inorder[:root_index]) root.right = build_tree_from_pre_in(preorder[root_index + 1:], inorder[root_index + 1:]) return root def postorder_traversal(root): if root is None: return [] return postorder_traversal(root.left) + postorder_traversal(root.right) + [root.data] # 输入示例 preorder = ['E', 'F', 'H', 'I', 'G', 'J', 'K'] inorder = ['H', 'F', 'I', 'E', 'J', 'K', 'G'] root = build_tree_from_pre_in(preorder, inorder) postorder_result = postorder_traversal(root) print("后序遍历结果:", postorder_result) ``` --- #### 练习题 3:完全二叉树层序遍历 已知一棵完全二叉树的高度为 4,第一层有 1 个节点,第二层有 2 个节点,第三层有 4 个节点,第四层有 8 个节点。若该二叉树前序遍历为 `EACBDGF`,请写出其中序遍历后序遍历结果。 - **解答**: 根据前序遍历和完全二叉树的性质,可以逐步还原二叉树结构。最终得到中序遍历为 `ABCEDGF`,后序遍历为 `CBEGAFD`[^3]。 ```python # 示例代码实现完全二叉树遍历 from collections import deque def level_order_traversal(root): if not root: return [] queue = deque([root]) result = [] while queue: node = queue.popleft() result.append(node.data) if node.left: queue.append(node.left) if node.right: queue.append(node.right) return result # 构建完全二叉树(假设已知前序遍历) preorder = ['E', 'A', 'C', 'B', 'D', 'G', 'F'] inorder = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] root = build_tree(preorder, inorder) # 层序遍历 level_order_result = level_order_traversal(root) print("层序遍历结果:", level_order_result) # 中后序遍历 inorder_result = inorder_traversal(root) postorder_result = postorder_traversal(root) print("中序遍历结果:", inorder_result) print("后序遍历结果:", postorder_result) ``` --- #### 练习题 4:表达式树的遍历 已知一棵表达式树的前序遍历为 `-+a*bc/de`,中序遍历为 `a+b*c-d/e`,请写出该表达式树的后序遍历结果。 - **解答**: 根据前序遍历和中序遍历的特点,可以逐步还原表达式树的结构。最终得到后序遍历结果为 `abc*+de/-`[^2]。 ```python # 示例代码实现表达式树的遍历 class ExpressionTreeNode: def __init__(self, data): self.data = data self.left = None self.right = None def build_expression_tree(preorder, inorder): if not preorder or not inorder: return None root_val = preorder[0] root = ExpressionTreeNode(root_val) root_index = inorder.index(root_val) root.left = build_expression_tree(preorder[1:root_index + 1], inorder[:root_index]) root.right = build_expression_tree(preorder[root_index + 1:], inorder[root_index + 1:]) return root def postorder_traversal(root): if root is None: return [] return postorder_traversal(root.left) + postorder_traversal(root.right) + [root.data] # 输入示例 preorder = ['-','+','a','*','b','c','/','d','e'] inorder = ['a','+','b','*','c','-','d','/','e'] root = build_expression_tree(preorder, inorder) postorder_result = postorder_traversal(root) print("后序遍历结果:", ''.join(postorder_result)) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值