返回

深度解析 589. N 叉树的前序遍历:递归、非递归,通用非递归全掌握

后端

## 前言

589. N 叉树的前序遍历 是 LeetCode 上的一道简单难度算法题,考察了树的遍历算法的应用。给定一个 N 叉树,要求以先序遍历的方式输出其节点值。先序遍历是一种深度优先搜索算法,它以递归或非递归的方式沿着树的每个节点深度遍历,先访问根节点,然后访问其所有子节点。

## 递归方法

递归是解决树遍历问题的一种常见方法。以下是使用递归实现 N 叉树的前序遍历的 Python 代码:

def preorder(root):
  """
  Perform preorder traversal on an N-ary tree.

  Parameters:
    root: The root node of the N-ary tree.

  Returns:
    A list of the values of the nodes in the N-ary tree, in preorder.
  """

  if not root:
    return []

  result = [root.val]  # Add the value of the root node to the result.

  # Recursively perform preorder traversal on each child of the root node.
  for child in root.children:
    result += preorder(child)

  return result

在代码中,preorder() 函数接受一个 N 叉树的根节点作为参数,并返回一个包含 N 叉树中节点值的有序列表。函数首先检查根节点是否为空,如果是,则直接返回一个空列表。然后,它将根节点的值添加到结果列表中。接着,函数递归地对根节点的每个子节点调用 preorder() 函数,并将每个子节点遍历的结果添加到结果列表中。最后,函数返回结果列表。

## 非递归方法

除了递归方法之外,还可以使用非递归的方式来实现 N 叉树的前序遍历。以下是非递归版本的 Python 代码:

def preorder_non_recursive(root):
  """
  Perform preorder traversal on an N-ary tree non-recursively.

  Parameters:
    root: The root node of the N-ary tree.

  Returns:
    A list of the values of the nodes in the N-ary tree, in preorder.
  """

  if not root:
    return []

  result = []  # Initialize the result list.
  stack = [root]  # Initialize the stack with the root node.

  # While the stack is not empty, pop the top node from the stack and add its value to the result list.
  # Then, push all of its children onto the stack.
  while stack:
    node = stack.pop()
    result.append(node.val)
    for child in node.children[::-1]:
      stack.append(child)

  return result

在代码中,preorder_non_recursive() 函数也接受一个 N 叉树的根节点作为参数,并返回一个包含 N 叉树中节点值的有序列表。函数首先检查根节点是否为空,如果是,则直接返回一个空列表。然后,它将根节点推入堆栈中。接着,函数从堆栈中弹出栈顶节点,并将它的值添加到结果列表中。然后,函数将该节点的所有子节点反向推入堆栈中。函数重复这个过程,直到堆栈为空。最后,函数返回结果列表。

## 通用非递归方法

以上两种方法都只适用于 N 叉树。但是,我们可以使用一种通用的非递归方法来遍历任意类型的树,包括二叉树、三叉树和多叉树。这种方法被称为 Morris 遍历 。Morris 遍历不需要使用额外的空间来存储节点,并且它的时间复杂度与递归方法相同。

以下是用 Python 实现的 Morris 遍历的代码:

def morris_traversal(root):
  """
  Perform preorder traversal on a tree using the Morris traversal algorithm.

  Parameters:
    root: The root node of the tree.

  Returns:
    A list of the values of the nodes in the tree, in preorder.
  """

  if not root:
    return []

  result = []  # Initialize the result list.
  current = root  # Initialize the current node to the root node.

  # While the current node is not None, loop through the following steps:
  while current:
    # If the current node has no left child, add its value to the result list and move to its right child.
    if not current.left:
      result.append(current.val)
      current = current.right

    # Otherwise, find the rightmost node in the current node's left subtree and make it point to the current node.
    else:
      predecessor = current.left
      while predecessor.right and predecessor.right != current:
        predecessor = predecessor.right

      # If the right child of the predecessor is None, make it point to the current node and move to the current node's left child.
      if not predecessor.right:
        predecessor.right = current
        current = current.left

      # Otherwise, reset the right child of the predecessor to None and add the current node's value to the result list.
      else:
        predecessor.right = None
        result.append(current.val)
        current = current.right

  return result

在代码中,morris_traversal() 函数接受一个树的根节点作为参数,并返回一个包含树中节点值的有序列表。函数首先检查根节点是否为空,如果是,则直接返回一个空列表。然后,它将根节点设为当前节点。接着,函数进入一个循环,在循环中,它根据当前节点的情况执行以下操作:

  • 如果当前节点没有左子节点,则将它的值添加到结果列表中,然后移动到它的右子节点。
  • 否则,找到当前节点的左子树中最右边的节点,并让它指向当前节点。
  • 如果最右边的节点的右子节点为空,则让它指向当前节点,然后移动到当前节点的左子节点。
  • 否则,将最右边的节点的右子节点设为空,将当前节点的值添加到结果列表中,然后移动到当前节点的右子节点。

函数重复这个过程,直到当前节点为空。最后,函数返回结果列表。

## 结论

  1. N 叉树的前序遍历是 LeetCode 上的一道简单难度算法题,考察了树的遍历算法的应用。这篇文章介绍了三种遍历 N 叉树的方法:递归、非递归和通用非递归。通过这些方法,我们可以对 N 叉树进行深度优先搜索,并输出其节点值的有序列表。