Python神技:用极简代码征服LeetCode各种树的遍历算法

前言

当我们在编程之旅中遇到树数据结构时,经典的问题通常涉及树的遍历算法。本文将使用Python尝试在十行代码以内解决大部分树的遍历问题,探讨如何高效解决LeetCode上与二叉树和N叉树相关的遍历问题。这些算法是每位程序员的必备技能,它们不仅在面试中频繁出现,而且在实际编码中也非常有用。让我们开始吧!
欢迎订阅LeetCode高分题解CSDN专栏,每日一题,和博主一起进步
LeetCode专栏
个人题解GitHub连接:LeetCode-Go-Python-Java-C

在这里插入图片描述

文章目录

  • 前言
  • 二叉树和N叉树的遍历算法
    • 二叉树
      • 二叉树的前序遍历
      • 二叉树的中序遍历
      • 二叉树的后序遍历
      • 二叉树的层序遍历
      • 二叉树的层序遍历 II
      • 二叉树的锯齿形层序遍历
      • 二叉树的垂序遍历
        • 超高分解法
      • 翻转二叉树以匹配先序遍历
    • N 叉树
      • N 叉树的前序遍历
      • N 叉树的后序遍历
      • N 叉树的层序遍历
  • 总结

二叉树和N叉树的遍历算法

在这篇文章中,我们将探讨使用Python解决LeetCode上关于二叉树和N叉树的遍历算法。这些算法是树数据结构中的经典问题,我们将展示如何使用Python代码以极简而高效的方式解决它们。

二叉树

# 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

二叉树的前序遍历

LeetCode链接

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

二叉树前序遍历算法

给定一个二叉树的根节点 root,实现二叉树的前序遍历。

算法思路:

  1. 如果根节点root为空,直接返回空列表[]。

  2. 初始化结果列表res = []。

  3. 定义前序遍历函数preorder(root),传入根节点:

    • 将root的值加入res

    • 递归调用preorder遍历左子树

    • 递归调用preorder遍历右子树

  4. 调用preorder(root),完成前序遍历。

  5. 返回res。

算法分析:

  • 时间复杂度 O(n),遍历整棵树的所有节点。

  • 空间复杂度 O(n),递归函数栈空间占用。

前序遍历顺序为根节点->左子树->右子树,递归实现了该遍历逻辑,代码简洁明了。

二叉树的中序遍历

LeetCode链接

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)

中序遍历二叉树算法

给定一个二叉树的根节点root,实现二叉树的中序遍历。

算法思路:

  1. 如果根节点root为空,直接返回空列表[]。

  2. 初始化结果列表res = []。

  3. 定义中序遍历函数inorder(root):

    • 递归调用inorder遍历左子树,将结果加入左列表

    • 将root的值加入res

    • 递归调用inorder遍历右子树,将结果加入右列表

  4. 调用inorder(root)进行中序遍历。

  5. 返回res。

算法分析:

  • 时间复杂度 O(n),遍历树的所有节点。

  • 空间复杂度 O(n),递归函数栈空间占用。

中序遍历顺序为左子树->根节点->右子树。递归实现了中序遍历逻辑,代码简洁正确。

二叉树的后序遍历

LeetCode链接

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

后序遍历二叉树算法

给定一个二叉树的根节点root,实现二叉树的后序遍历。

算法思路:

  1. 如果根节点root为空,直接返回空列表[]。

  2. 初始化结果列表 res = []。

  3. 定义后序遍历函数 postorder(root):

    • 递归调用postorder遍历左子树,将结果加入左列表

    • 递归调用postorder遍历右子树,将结果加入右列表

    • 将root加入到res中

  4. 调用postorder(root)进行后序遍历。

  5. 返回res。

算法分析:

  • 时间复杂度 O(n) ,遍历树的所有节点。

  • 空间复杂度 O(n),递归函数栈空间占用。

后序遍历顺序为左子树->右子树->根节点。递归实现了后序遍历逻辑,代码简洁正确。

二叉树的层序遍历

LeetCode链接

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        deq, rst = deque([(root, 1)]), []
        while deq:
            node, level = deq.popleft()
            if node:
                rst[-1].append(node.val) if len(rst) >= level else rst.append([node.val])
                deq.extend([(node.left, level + 1), (node.right, level + 1)])
        return rst

二叉树层序遍历算法

给定一个二叉树的根节点 root,实现二叉树的层序遍历。

算法思路:

  1. 初始化结果列表 res = [] 和队列 deque。

  2. 将根节点入队,记录层数 level = 1。

  3. 当队列不为空时:

    • 节点出队,记录为 node

    • 判断节点是否为空

      • 如果不为空,根据层数决定将节点值加入对应的 res 列表

      • 将左右子节点入队,层数 +1

  4. 返回 res

算法分析:

  • 时间复杂度 O(N): 遍历全部节点

  • 空间复杂度 O(N): 队列和结果列表占用线性空间

使用队列的先入先出特性,记录层数,实现了层序遍历逻辑。

二叉树的层序遍历 II

LeetCode链接

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        deq, rst = deque([(root, 1)]), []
        while deq:
            node, level = deq.popleft()
            if node:
                rst[-level].append(node.val) if len(rst) >= level else rst.insert(0, [node.val])
                deq.extend([(node.left, level + 1), (node.right, level + 1)])
        return rst

二叉树逆序层序遍历算法

给定一个二叉树的根节点 root,实现从底层到顶层的逆序层序遍历。

算法思路:

  1. 初始化结果列表 res = [] 和队列 deque

  2. 将根节点入队,记录层数 level = 1

  3. 当队列不为空时:

    • 节点出队,记录为 node

    • 判断节点是否为空

      • 如果不为空,根据层数将节点值插入到res前端

      • 将左右子节点入队,层数+1

  4. 返回res

算法分析:

  • 时间复杂度 O(N):遍历全部节点

  • 空间复杂度 O(N):队列和res列表占用线性额外空间

维护res的插入顺序实现了逆序层序遍历,思路新颖。

二叉树的锯齿形层序遍历

LeetCode链接

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        def preorder(node, level):
            if not node:
                return
            if len(ans) < level + 1:
                ans.append([])
            ans[level].append(node.val)
            preorder(node.left, level + 1)
            preorder(node.right, level + 1)        
        preorder(root, 0)
        for i in range(1, len(ans), 2):
            ans[i].reverse()
        return ans

二叉树锯齿形层序遍历算法

给定一个二叉树的根节点 root,实现二叉树的锯齿形层序遍历。

算法思路:

  1. 初始化结果列表 res = []。

  2. 定义递归函数 preorder,传入当前节点和层数 level。

    • 如果节点为空,直接返回

    • 如果 res 大小小于 level+1,代表需要新建层级列表,则追加空列表到 res

    • 将当前节点值加入列表 res[level] 中

    • 递归遍历左右子节点,层数 +1

  3. 先序遍历结束后,按层序从 1 开始,每隔一层翻转列表。

  4. 返回 res。

算法分析:

  • 时间复杂度 O(N):遍历全部节点

  • 空间复杂度 O(N):存储 res 需线性额外空间

递归实现顺序层序遍历,再翻转某些层列表,巧妙地实现了锯齿形层序遍历。

二叉树的垂序遍历

LeetCode链接

class Solution:
    def verticalTraversal(self, root: TreeNode) -> List[List[int]]:
        def dfs(node, row, col):
            if not node: return []
            column_nodes[col].append((row, node.val))
            return dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1)
        if not root: return []
        column_nodes = defaultdict(list)
        dfs(root, 0, 0)
        return [[val for _, val in sorted(nodes)] for col, nodes in sorted(column_nodes.items())]

二叉树垂直遍历算法

给定一个二叉树的根节点 root,返回这个二叉树的垂直遍历。

算法思路:

  1. 定义dfs递归函数,遍历每个节点,记录节点的行和列信息(row, col)。

  2. 使用字典column_nodes,key为列号col,value为(行号row,节点值val)的列表。

  3. 在dfs遍历时,将(row, node.val)追加到对应的列column_nodes[col]。

  4. dfs递归遍历整棵树,记录所有节点的行列信息。

  5. 对每一列遍历排序,再将节点值取出,即为该列的垂直遍历结果。

  6. 返回column_nodes.values()。

算法分析:

  • 时间复杂度 O(NlogN): DFS遍历树O(N), 每列排序O(logN)

  • 空间复杂度 O(N): 字典存储节点行列信息占用线性空间

通过DFS记录行列信息,自定义排序,巧妙地实现了二叉树的垂直遍历。

超高分解法

Python神技:用极简代码征服LeetCode各种树的遍历算法_第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
from collections import defaultdict

class Solution:
    def verticalTraversal(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []

        # 使用哈希表存储每列的节点,key为列号,value为该列的节点列表
        column_nodes = defaultdict(list)
        
        def dfs(node, row, col):
            if not node:
                return
            # 使用深度优先搜索遍历树
            column_nodes[col].append((row, node.val))
            dfs(node.left, row + 1, col - 1)
            dfs(node.right, row + 1, col + 1)
        
        # 从根节点开始深度优先搜索
        dfs(root, 0, 0)
        
        # 将哈希表中的节点按列索引排序,并构建结果列表
        sorted_columns = sorted(column_nodes.items())
        
        return [
            [val for _, val in sorted(nodes)]
            for col, nodes in sorted_columns
        ]

二叉树垂直遍历算法

给定一个二叉树的根节点 root,返回这个二叉树的垂直遍历。

算法思路:

  1. 使用哈希表 column_nodes 记录每列的节点,键为列号,值为该列的(行号,节点值)列表。

  2. 使用 dfs 对树进行深度优先遍历,记录每个访问节点的行列信息,更新到哈希表。

  3. 对哈希表中的列进行排序,每列内也按行号排序。

  4. 构建结果列表,每个子列表对应一列的节点值。

  5. 返回结果列表。

复杂度分析:

  • 时间复杂度 O(NlogN):遍历树需 O(N),每列排序需 O(logN)。

  • 空间复杂度 O(N):哈希表存储树全部节点。

该算法通过哈希表与 DFS 遍历树,高效实现了二叉树的垂直遍历。

翻转二叉树以匹配先序遍历

LeetCode链接

class Solution:
    def flipMatchVoyage(self, root: TreeNode, voyage: List[int]) -> List[int]:
        def dfs(node):
            nonlocal idx
            if not node:
                return True
            if node.val != voyage[idx]:
                return False
            idx += 1
            if node.left and node.left.val != voyage[idx]:
                node.left, node.right = node.right, node.left
                result.append(node.val)
            return dfs(node.left) and dfs(node.right)
        idx, result = 0, []
        return result if dfs(root) else [-1]

二叉树按顺序翻转算法

给定二叉树根节点 root 和指定翻转顺序 voyage,返回进行翻转操作后的结果。

算法思路:

  1. 定义 dfs 递归函数,传入当前节点。

  2. 如果节点值不等于 voyage[idx],返回 False。

  3. 记录 voyage 的索引 idx。如果左子节点值不等于 voyage[idx],交换该节点的左右子树。

  4. 将交换的节点值加入 result。

  5. 递归左右子树,如果全部遍历成功,返回 True。

  6. 如果 dfs 返回 True,则顺序有效,返回 result。否则返回 [-1]。

复杂度分析:

  • 时间复杂度 O(N):遍历树的全部节点。

  • 空间复杂度 O(N):递归栈空间占用 O(h),h 为树高。

通过递归、交换左右子树,巧妙地实现了按指定顺序进行翻转的要求。

N 叉树

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

N 叉树的前序遍历

LeetCode链接

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        return [] if not root else [root.val] + [x for child in root.children for x in self.preorder(child)]

N叉树前序遍历

给定一个 N 叉树的根节点 root,返回这个 N 叉树的前序遍历。

算法思路:

  1. 定义递归函数 preorder,输入根节点 root。

  2. 终止条件:如果根节点为空,返回空列表[]。

  3. 处理当前层:将根节点值 root.val 加入结果列表。

  4. 递归调用:遍历 root 的所有子节点 children,并将递归返回结果展开。

  5. 返回值:返回加入了根节点的列表。

复杂度分析:

  • 时间复杂度 O(N):遍历树的全部节点。

  • 空间复杂度 O(N):递归栈空间为树高度,最坏为 N。

该算法通过递归展开子节点前序结果,简洁地实现了 N 叉树的前序遍历。

N 叉树的后序遍历

LeetCode链接

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        return [] if not root else [x for child in root.children for x in self.postorder(child)] + [root.val]

N叉树后序遍历算法

给定一个 N 叉树的根节点 root,返回这个 N 叉树的后序遍历。

算法思路:

  1. 定义递归函数 postorder,输入根节点 root。

  2. 终止条件:如果根节点为空,返回空列表 []。

  3. 递归调用:先递归遍历 root 的所有子节点,并展开结果列表。

  4. 处理根节点:将根节点值 root.val 加入列表末尾。

  5. 返回值:返回先处理子节点然后加入根的结果列表。

复杂度分析:

  • 时间复杂度 O(N):访问全部节点。

  • 空间复杂度 O(N):递归栈空间,最坏为树高度。

该算法通过递归先处理子节点然后根节点,简洁地实现了 N 叉树的后序遍历。

N 叉树的层序遍历

LeetCode链接

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []
        result, queue = [], deque([(root, 1)])
        while queue:
            node, level = queue.popleft()
            if len(result) < level:
                result.append([])
            result[level - 1].append(node.val)
            queue.extend([(child, level + 1) for child in node.children])
        return result

N叉树层序遍历算法

给定一个 N 叉树的根节点 root,返回这个 N 叉树的层序遍历结果。

算法思路:

  1. 初始化结果列表 result 和队列 queue,根节点入队。

  2. 当队列不空时,弹出队首元素,获取节点和层数。

  3. 如果结果列表大小小于层数,在结果列表增加一层。

  4. 将当前节点值加入当前层结果列表。

  5. 将当前节点的子节点入队到队尾,层数+1。

  6. 重复步骤 2-5,直到队列为空。

  7. 返回结果列表 result。

复杂度分析:

  • 时间复杂度 O(N):遍历全部节点。

  • 空间复杂度 O(N):队列存储全部节点。

该算法通过队列实现了 N 叉树的层序遍历。

总结

在编程生涯中,树结构是一个常见且强大的数据结构,它经常出现在算法和面试题中。掌握这些树的遍历算法对于每位程序员来说都是重要的技能。通过使用递归、队列等数据结构,我们可以轻松解决各种与树相关的问题,包括二叉树和N叉树的前序、中序、后序和层序遍历,以及一些特殊情况下的问题。希望这些示例代码有助于你更好地理解和掌握这些算法。如果你有任何问题或需要进一步的帮助,欢迎提问!

你可能感兴趣的:(python,LeetCode,算法,python,leetcode)