返回

用JavaScript征服Leetcode 144:二叉树的前序扫掠

前端

作为JavaScript爱好者,我们着迷于探索算法的优雅。Leetcode上的第144题-二叉树的前序扫掠是一道广受欢迎的问题,让我们携手探索它的解决方案,用JavaScript的魔法赋予其生命。

前序扫掠:理解基础

在二叉树中,前序扫掠是一种特殊的树搜索技术,其访问次序为:根节点、左子树、右子树。它在树结构的表示和数据处理中有着重要的应用,因此对于计算机科学家和程序员来说,掌握前序扫掠至关重要。

方法一:基于堆栈的优雅实现

为了使用JavaScript征服第144题,我们的第一个方法是借助栈的数据结构。栈是一种“先进先出”的数据结构,为我们的前序扫掠提供了一个天然的框架。算法的步骤如下:

  1. 将根节点推入栈中。
  2. 只要栈不为空,执行以下步骤:
    • 弹出栈顶元素并访问它。
    • 如果该节点有左子树,将左子树推入栈中。
    • 如果该节点有右子树,将右子树推入栈中。

方法二:非基于堆栈的高效实现

虽然基于堆栈的实现简洁优雅,但它在某些情况下可能会导致栈内存溢出。因此,我们探索一种非基于堆栈的实现,它效率更高,并且在处理大树时能够更好地控制内存使用。

  1. 当前节点指向根节点。
  2. 如果当前节点不为空,则访问它。
  3. 如果当前节点有左子树,则让当前节点指向左子树。
  4. 否则,如果当前节点有右子树,则让当前节点指向右子树。
  5. 否则,让当前节点指向当前节点的父节点。
  6. 重复步骤2-5,直到当前节点为null。

时间和空间复杂度

这两种方法的时间复杂度都是O(n),其中n是二叉树中的节点数。这是因为我们访问了每个节点一次且只访问一次。

空间复杂度方面,基于堆栈的实现为O(n),因为在最坏情况下,它将整个树存储在栈中。然而,非基于堆栈的实现只使用常数空间,因为它只存储少量指针。

用JavaScript征服Leetcode

在JavaScript中实现这两个算法非常简单。以下是一个基于堆栈的实现示例:

const preorderTraversal = (root) => {
  const stack = [];
  const result = [];

  if (root) {
    stack.push(root);
  }

  while (stack.length) {
    const node = stack.pop();
    result.push(node.val);

    if (node.left) {
      stack.push(node.left);
    }

    if (node.right) {
      stack.push(node.right);
    }
  }

  return result;
};

以下是非基于堆栈的实现示例:

const preorderTraversal = (root) => {
  const result = [];
  let current = root;

  while (current) {
    result.push(current.val);

    if (current.left) {
      current = current.left;
    } else if (current.right) {
      current = current.right;
    } else {
      while (current.parent && !current.parent.left) {
        current = current.parent;
      }

      if (current.parent) {
        current = current.parent.right;
      } else {
        current = null;
      }
    }
  }

  return result;
};

总结

通过深入了解前序扫掠的算法,并用JavaScript优雅地实现了基于堆栈和非基于堆栈的解决方案,我们征服了Leetcode的第144题。现在,我们不仅掌握了算法本身,而且还理解了其背后的细微差别,从而为我们解决更复杂的算法问题做好了准备。