一道经典算法题:翻转二叉树

331 阅读2分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。

来做一道经典算法题,翻转二叉树(invert a binary tree),据说因为这道题,谷歌在面试中干掉了 homebrew 创始者 Max Howell。

Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.

翻转二叉树

真题描述:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

image.png

递归(dfs 遍历)

图解:

翻转二叉树.gif

  • 递归遍历整棵树,交换左右叶子节点的位置
  • 终止条件:当前节点为 null 时返回

代码实现如下:

const invertTree = function(root) {
    if (root === null) {
        return null;
    }
    const left = invertTree(root.left);
    const right = invertTree(root.right);
    root.left = right;
    root.right = left;
    return root;
};

时间复杂度:O(n),n 为二叉树节点的数目,因为我们会遍历整个二叉树。
空间复杂度:O(n),n 为递归调用栈的深度,也是当前节点在二叉树中的高度,最好为 O(logn),最坏为 O(n)。

在 JS 中,可以不用声明临时变量,使用数组结构赋值完成元素的交换,

const invertTree = function (root) {
  if (root === null) {
    return null
  }
  [root.left, root.right] = [invertTree(root.right), invertTree(root.left)]
  return root
}

这么写代码就要简洁很多。

bfs

既然是遍历整棵树,当然也可以用 bfs 的思路来解决,

定义一个队列,将根节点放入到队列中,然后不断的迭代队列中的元素。

对当前元素调换其左右子树的位置,然后:

  • 判断其左子树是否为空,不为空就放入队列中
  • 判断其右子树是否为空,不为空就放入队列中
const invertTree = function (root) {
  if (root == null) return null

  const queue = []
  queue.unshift(root)

  while (queue.length) {
    const node = queue.shift()

    if (node.left) {
      queue.push(node.left)
    }
    if (node.right) {
      queue.push(node.right)
    }

    [node.left, node.right] = [node.right, node.left]
  }
  return root
}

小结

其实这道题考的是二叉树的遍历,一想到遍历,就能想到 dfs 和 bfs,再套公式一样地完成题解就行了,关于 dfs 和 bfs,更多可了解我的这两篇文章:广度优先搜索 深度优先搜索

往期

从 keep-alive 源码掌握 LRU Cache

为了看懂 Vue3 diff算法,我学习了JS 位运算