首页 > 其他分享 >刷题记录(回顾)二叉树-2,3,4,5 二叉树的各种遍历

刷题记录(回顾)二叉树-2,3,4,5 二叉树的各种遍历

时间:2025-01-08 16:33:03浏览次数:3  
标签:node 遍历 val self right 二叉树 left root 刷题

二叉树共有两类遍历方式(理解前中后序+层序)

DFS:深度优先搜索:即前中后三序遍历

所谓前中后序就是:

“左”,“中”,“右”这三个元素组成的排列中“中”的位置,

中代表处理节点,

左代表访问左孩子

右代表访问右孩子

        前序遍历:中左右,先处理节点后访问左右孩子

        中序遍历:左中右,先访问左孩子,再处理节点,后访问右孩子

        后序遍历:左右中,先访问左右孩子,后处理节点

PS:

一颗二叉树有唯一的三序遍历与其对应,

但某一个三序遍历序列并不一定存在唯一一颗二叉树与之对应,

有点像一向箔把一个二维实体一维话的感觉,

三序遍历产生的序列其实可以看做一个个的区域组成,

每个区域(特定子序列)都有原二叉树的某个子树与之对应,

但问题就是你找不到各个区域的分界点在哪,

所以只能单向映射

BFS:广度优先搜索:

        层序遍历:一般由队列实现

个人感觉二叉树的遍历是二叉树中最重要的部分

最好能练到肌肉记忆的程度

一、二叉树的深度优先遍历

1.递归

模板都一样,

区别仅在于访问左孩子、访问右孩子和处理节点的顺序:

前序:

144. 二叉树的前序遍历

# 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: Optional[TreeNode]) -> List[int]:
        ans = []

        def dfs(root):
            if not root:
                return
            
            ans.append(root.val)
            dfs(root.left)
            dfs(root.right)
        
        dfs(root)
        return ans

中序:

94. 二叉树的中序遍历

# 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 inorder(self, root, res):
        if not root:
            return
        
        self.inorder(root.left, res)
        res.append(root.val)
        self.inorder(root.right, res)

    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        self.inorder(root, res)
        return res

后续:

145. 二叉树的后序遍历

# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        
        def dfs(root):
            if not root:
                return
            
            dfs(root.left)
            dfs(root.right)
            ans.append(root.val)
        
        dfs(root)
        return ans

当然也可以写成先判断左右孩子是否存在再访问的形式

2.迭代

2.1 前序法

(代码随想录 二叉树-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: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans = []

        stack = [root]
        while stack:
            node = stack.pop()
            ans.append(node.val)
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        
        return ans
实现后续遍历:
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans = []

        stack = [root]
        while stack:
            node = stack.pop()
            ans.append(node.val)
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
        
        return ans[::-1]

2.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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans, stack = [], []

        cur, prev = root, None
        while cur or stack:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            if not cur.right or cur.right == prev:
                ans.append(cur.val)
                prev = cur
                cur = None
            else:
                stack.append(cur)
                cur = cur.right
        
        return ans

然而,这种逻辑真的只能用于实现中序遍历吗?

显然不是的,

事实上这种逻辑不仅能实现中序遍历,

不要忘了,需要第二次访问一个节点的序列还有后续遍历,

但和实现中序遍历不同的是,

实现后续遍历需要访问左右孩子后再访问父节点,

所以需要第二个节点来记录

实现后序遍历:
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans, stack = [], []

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

        return ans

当然了,前序遍历也可以用指针法实现,

只是没有必要,

但写一写感觉对于理解指针法中的每一行代码是在做什么是有帮助的,

实现前序遍历:
# 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: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans, stack = [], []

        node = root
        while node or stack:
            if node:
                ans.append(node.val)
                stack.append(node)
                node = node.left
            else:
                node = stack.pop()
                node = node.right
        
        return ans

 此外,

指针法的向左探底的逻辑有两种写法:

一种是用if,比较好理解,本文的前序和后序用的是if写法

另一种是用while,本文的中序用的就是while写法

2.3 统一迭代法

(代码随想录二叉树-4)

统一迭代法的代码逻辑是每个节点都访问两次

每次分别做的不一样的事情

第一次,反序打标记:

每当第一次访问到一个节点,

就把当前节点极其左右孩子按照对于顺序(前后中)的反序压入栈中

第二次,处理节点:

每当遇到打好标记的节点,

便处理节点

这样利用栈的反序特性

第二次访问的时候也就是处理节点的顺序就是对应顺序的遍历

打标记的方法有两种:

空写法:一种是将当前节点压入栈后立即压入一个空节点,

让空节点充当标记

元组写法:另一种是使用多个栈或元组(python)的方式用true和false打标签,

未访问标false,

访问过的标true:

实现前序遍历(空写法):
# 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: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        ans = []

        stack = [root]
        while stack:
            node = stack.pop()
            if node:
                if node.right:
                    stack.append(node.right)
                if node.left:
                    stack.append(node.left)
                stack.append(node)
                stack.append(None)
            else:
                node = stack.pop()
                ans.append(node.val)
        
        return ans
实现中序遍历(元组写法):
# 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]:
        if not root: # 左中右
            return []
        ans = []

        stack = [(root, False)]
        while stack:
            node, visited = stack.pop()
            if not visited:
                if node.right:
                    stack.append((node.right, False)) # 右
                stack.append((node, True)) # 中
                if node.left:
                    stack.append((node.left, False)) # 左
            else:
                ans.append(node.val)
        
        return ans
实现后序遍历(空写法):
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root: # 左右中
            return []
        ans = []

        stack = [root]
        while stack:
            node = stack.pop()
            if node:
                stack.append(node) # 中
                stack.append(None)
                if node.right:
                    stack.append(node.right) # 右
                if node.left:
                    stack.append(node.left) # 左
            else:
                node = stack.pop()
                ans.append(node.val)
        
        return ans

二、二叉树的广度优先遍历(层序遍历)

102. 二叉树的层序遍历

层序遍历的关键逻辑在于:

每访问一层求一次长度,

然后层内for循环访问节点,

代码实现:
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        ans = []
        que = collections.deque()

        que.append(root)
        while que:
            n, cur = len(que), []
            for i in range(n):
                node = que.popleft()
                cur.append(node.val)
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
            ans.append(cur)
        
        return ans

附录:安装方式(如果手生了或忘了)

简易安装:

三序递归(简单)+ 前序遍历:前序法迭代(简单) + 三序统一迭代法(一法三用)+ 层序遍历

完全安装:

1.三序递归+

2.三序统一迭代法

3.层序遍历

4.其他三序迭代法

前序遍历:前序法 + 指针法

中序遍历:指针法

后序遍历:前序法 + 指针法

标签:node,遍历,val,self,right,二叉树,left,root,刷题
From: https://blog.csdn.net/weixin_44610684/article/details/144970870

相关文章

  • 数据结构与算法之二叉树: LeetCode 107. 二叉树的层序遍历 II (Ts版)
    二叉树的层序遍历IIhttps://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/描述给你二叉树的根节点root,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)示例1输入:root=[3,9,20,null,nul......
  • 打卡信奥刷题(561)用C++信奥P7343[普及组/提高] 【DSOI 2021】电子跃迁
    【DSOI2021】电子跃迁题目背景“如果能证明大统一理论,这个世界将焕然一新。”“量子……量子……就差一点……”“嘶……哦。我想我明白了。”题目描述在你的视野下,出现了一排电子,他们分别拥有不同的能量。你需要做的是通过将相邻电子互换的方法,将电子排的有序。有......
  • Windows bat批处理用for遍历、循环、查找的变量不能在for外用
    前言全局说明Windowsbat批处理用for遍历、循环、查找的变量不能在for外用Windowsbat不像Linuxshell有很完善的语法,bat中除了判断,很多查询或要遍历的东西都要用for完成。一、说明1.1环境:Windows二、for循环变量下面的写法,for循环外是获取不到file,因......
  • 代码随想录:二叉树的递归遍历
    代码随想录:二叉树的递归遍历现在是找借口时间,一开始是期末考试太忙了,后来是过年放假,一晃这么久没写题了,这样不好。,看了一下我现在leetcode才40多道题呢定个目标,三月之前刷完代码随想录,并且把hot100的简单中等题都写了。/***Definitionforabinarytreenode.*structTre......
  • C语言数据结构与算法(二叉树)
    1.二叉树的概念及结构1.1概念一棵二叉树是结点的一个有限集合,该集合:1.或者为空2.由一个根节点加上两棵别称为左子树和右子树的二叉树组成特性:1.二叉树不存在度大于2的结点2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树1.2特殊的二叉树满二叉树:每......
  • 【Leetcode_Hot100】二叉树
    二叉树94.二叉树的中序遍历104.二叉树的最大深度226.翻转二叉树101.对称二叉树543.二叉树的直径102.二叉树的层序遍历108.将有序数组转换为二叉搜索树98.验证二叉搜索树230.二叉搜索树中第K小的元素199.二叉树的右视图114.二叉树展开为链表105.从前序与......
  • 【Day 11 LeetCode】二叉树的遍历
    一、二叉树的遍历二叉树的遍历主要分为深度优先遍历和广度优先遍历。深度优先是先往深处走,走到尽头返回;广度优先遍历是一层一层往下遍历。其中,深度优先遍历对应三种顺序,前序、中序、后序遍历,特点也很好记,就是根节点的位置。根节点位于前面就是前序,遍历顺序为根节点-左子......
  • 力扣刷题:二叉树OJ篇(下)
    大家好,这里是小编的博客频道小编的博客:就爱学编程很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!目录1.(1)题目描述(2)解题思路2.对称二叉树(1)题目描述(2)解题思路3.另一棵树的子树(1)题目描述(2)解题思路4.二叉树的构建及遍历(1)题目描述(2......
  • 【代码随想录】刷题记录(91)-根据身高重建队列
    题目描述:假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i]=[hi,ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。请你重新构造并返回输入数组 people 所表示的队列。返回的......
  • 【代码随想录】刷题记录(92)-用最少数量的箭引爆气球
    题目描述:有一些球形气球贴在一堵用XY平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i]=[xstart,xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切y坐标。一支弓箭可以沿着x轴从不同点 完全垂直 地射出。在坐标 x ......