leetcode—19.二叉树遍历相关题目leetcode总结

文章目录引言一、深度优先遍历144. 二叉树的前序遍历94. 二叉树的中序遍历145. 二叉树的后序遍历二、广度优先遍历102. 二叉树的层序遍历103. 二叉树的锯齿形层序遍历引言  二叉树遍历即将二叉树按照某种顺序访问一遍,涉及深度优先遍历与广度优先遍历。深度优先遍历可分为前序遍历、中序遍历、后序遍历。广度优先遍历包括层次遍历,层序遍历可分为自顶向下层序遍历,自底向上层序遍历,锯齿层序遍历。一、深度优先遍历  一般都有递归与迭代思路,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显
摘要由CSDN通过智能技术生成

引言

  二叉树遍历即将二叉树按照某种顺序访问一遍,涉及深度优先遍历与广度优先遍历。深度优先遍历可分为前序遍历、中序遍历、后序遍历。广度优先遍历包括层次遍历,层序遍历可分为自顶向下层序遍历,自底向上层序遍历,锯齿层序遍历。

一、深度优先遍历

  一般都有递归与迭代思路,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同

144. 二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例:

输入:root = [1,null,2,3]
输出:[1,2,3]
在这里插入图片描述

思路1:递归

前序遍历操作定义为:若二叉树为空,为空操作;否则根 -> 左 -> 右

复杂度分析:

  1. 时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
  2. 空间复杂度:O(n),为递归过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为O(n)。
# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []

        result = [root.val]
        # 递归调用左节点
        left_item = self.preorderTraversal(root.left)
        # 递归调用右节点
        right_item = self.preorderTraversal(root.right)
        return result + left_item + right_item

思路2:迭代

递归算法使用系统栈,不好控制,性能问题比较严重,需要进一步了解不用递归如何实现。
为了维护固定的访问顺序,使用数据结构的后入先出特性。

先处理根节点,根据访问顺序根→左→右,先入栈的后访问,为了保持访问顺序(先入后出),先把右孩子入栈,再入栈左孩子(此处需要注意:出栈才是访问顺序)
复杂度分析:

  1. 时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
  2. 空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。
# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []

        stack = [root]
        result = []
        while stack:
            # 弹出元素—后入先出
            node = stack.pop()
            if node != None:
                # 先入栈右节点
                stack.append(node.right)
                # 再入栈左节点
                stack.append(node.left)
                # 保存遍历的值(栈顶元素的值)
                result.append(node.val)
        return result

思路3:颜色标记法

其核心思想是使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色:
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、左子节点、自身依次入栈。
如果遇到的节点为灰色,则将节点的值输出。

颜色标记法的优势:兼具栈迭代方法的高效,又像递归方法一样简洁易懂,更重要的是,这种方法对于前序、中序、后序遍历,能够写出完全一致的代码。

# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
        """
        颜色标记法
        """
        if root == None:
            return []
        
        write,gray = 0,1
        stack = [(write,root)]
        result = []

        while stack:
            color,node = stack.pop()
            if node != None:
                # 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、左子节点、自身依次入栈。
                if color == write:
                    stack.append((write,node.right))
                    stack.append((write,node.left))
                    stack.append((gray,node))
                # 如果遇到的节点为灰色,则将节点的值输出。
                else:
                    result.append(node.val)
        return result

思路4:Morris遍历

Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。

  1. 新建临时节点,令该节点为 root;
  2. 如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点;
  3. 如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点:
    如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。
    如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = list()
        if not root:
            return res
        
        p1 = root
        while p1:
            p2 = p1.left
            if p2:
                while p2.right and p2.right != p1:
                    p2 = p2.right
                if not p2.right:
                    res.append(p1.val)
                    p2.right = p1
                    p1 = p1.left
                    continue
                else:
                    p2.right = None
            else:
                res.append(p1.val)
            p1 = p1.right
        
        return res

复杂度分析:

  1. 时间复杂度:O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。
  2. 空间复杂度:O(1)。只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间。

94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

思路1:递归

中序遍历操作定义为:若二叉树为空,为空操作(递归终止条件);否则左 -> 根 -> 右

# 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: TreeNode) -> List[int]:
        # 递归
        if root == None:
            return []

        result = [root.val]
        left_item = self.inorderTraversal(root.left) 
        right_item = self.inorderTraversal(root.right)

        return left_item + result + right_item

思路2:迭代

核心思路依旧是利用栈维护节点的访问顺序:左→根→右

  1. 访问当前节点,
    如果当前节点有左孩子,则把它的左孩子都入栈,移动当前节点到左孩子,重复第一步直到当前节点没有左孩子
  2. 当当前节点没有左孩子时,栈顶节点出栈,加入结果数组
  3. 当前节点指向栈顶节点的右节点
# 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: TreeNode) -> List[int]:
        # 迭代
        if root == None:
            return []
        
        stack 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值