算法题3:二叉树

3. 二叉树

3.1 前中后序遍历

首先,回顾一下 学习数据结构和算法的框架思维 中说到的二叉树遍历框架:

def traverse(root):
    if root is None:
        return
    # 前序位置
    traverse(root.left)
    # 中序位置
    traverse(root.right)
    # 后序位置

先不管所谓前中后序,单看 traverse 函数,你说它在做什么事情?
其实它就是一个能够遍历二叉树所有节点的一个函数,和你遍历数组或者链表本质上没有区别。

二叉树这种结构无非就是二叉链表,由于没办法简单改写成迭代形式,所以一般说二叉树的遍历框架都是指递归的形式。
你也注意到了,只要是递归形式的遍历,都可以有前序位置和后序位置,分别在递归之前和递归之后。

所谓前序位置,就是刚进入一个节点(元素)的时候,后序位置就是即将离开一个节点(元素)的时候,那么进一步,你把代码写在不同位置,代码执行的时机也不同:

  • 前序位置的代码在刚刚进入一个二叉树节点的时候执行;
  • 后序位置的代码在将要离开一个二叉树节点的时候执行;
  • 中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。

重点:但这里面大有玄妙,前序位置的代码只能从函数参数中获取父节点传递来的数据,而后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据
在这里插入图片描述

二叉树的所有问题,就是让你在前中后序位置注入巧妙的代码逻辑,去达到自己的目的,你只需要单独思考每一个节点应该做什么,其他的不用你管,抛给二叉树遍历框架,递归会在所有节点上做相同的操作

[104] 二叉树最大深度问题

class Solution(object):
    res = 0
    depth = 0
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        self.traverse(root)
        return self.res

    # 定义递归函数
    def traverse(self, root):
        # global res, depth
        if not root:
            return
        # 前序位置,进入下一层节点,深度加一
        self.depth += 1
        if not root.left and not root.right:
            # 到达叶子节点,更新最大深度
           self.res = max(self.res, self.depth)

        self.traverse(root.left)
        self.traverse(root.right)

        # 后序位置,离开该节点时深度减一
        self.depth -= 1

3.2 层序遍历

# 输入一棵二叉树的根节点,层序遍历这棵二叉树
def levelTraverse(root: TreeNode):
    if not root:
        return
    
    # 双端队列
    q = deque()
    q.append(root)

    # 从上到下遍历二叉树的每一层
    while q:
        sz = len(q)
        # 从左到右遍历每一层的每个节点
        for i in range(sz):
            cur = q.popleft()
            # 将下一层节点放入队列
            if cur.left:
                q.append(cur.left)
            if cur.right:
                q.append(cur.right)

这里面 while 循环和 for 循环分管从上到下和从左到右的遍历:
在这里插入图片描述

3.3 还原二叉树

根据前序和中序遍历结果,还原二叉树:

class Solution:
    def deduceTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        # 前序遍历:【根节点,左子树,右子树】
        # 中序遍历:【左子树,根节点,右子树】
        # 所以前序遍历的第一个元素肯定是根节点,然后找到中序遍历中的根节点,此时中序遍历的左边元素就是左子树元素,右边元素就是右子树元素
        # 然后在左右子树中重复这个过程即可
        if not preorder or not inorder:
            return None

        root = TreeNode(preorder[0])
        index = inorder.index(preorder[0])
        root.left = self.deduceTree(preorder[1:index + 1], inorder[0:index])
        root.right = self.deduceTree(preorder[index + 1:], inorder[index + 1:])
        return root

        # 时间复杂度:查询 index 下标是数组的遍历查询,时间复杂度为o(n), 递归分为左右子树,时间复杂度为log2(n), 此方法时间复杂度为 nlog2(n)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值