「算法」对称二叉树 | 刷题打卡

385 阅读3分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

原题链接 👉 101. 对称二叉树

示例 1:

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

二、思路分析:

思路1: 深度优先遍历

此题可以用深度优先遍历(递归)的思路求解。

比如看下面这两个子树(他们分别是根节点的左子树和右子树),能观察到这么一个规律:

  • 左子树 2的左孩子 == 右子树 2的右孩子;
  • 左子树 2 的右孩子 == 右子树 2的左孩子
   2         2
  / \       / \
 3   4     4   3
/ \ / \   / \ / \
8  7 6  5 5  6 7  8

我们将根节点的左子树记做 left,右子树记做 right。比较 left 是否等于 right,不等的话直接返回就可以了。 如果相等,比较 left 的左节点和 right 的右节点,再比较 left 的右节点和 right 的左节点

根据上面信息可以总结出递归函数的两个条件:

  1. 终止条件为:left 和 right 不等,或者 left 和 right 都为空
  2. 递归的比较 left,left 和 right.right,递归比较 left,right 和 right.left

思路2: 广度优先遍历

  1. 引入一个队列,这是把递归程序改写成迭代程序的常用方法。
  2. 初始化时我们把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像)
  3. 然后将两个结点的左右子结点按相反的顺序插入队列中。
  4. 当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束。

三、完整代码:

思路1: 深度优先遍历

function isSymmetric(root) {
  return isMirror(root.left, root.right)
  function isMirror(node1, node2) {
    if (node1 === null && node2 === null) {
      return true
    }
    if (node1 === null || node2 === null) {
      return false
    }
    return (
      node1.val === node2.val &&
      isMirror(node1.left, node2.right) &&
      isMirror(node1.right, node2.left)
    )
  }
}

复杂度分析

  • 时间复杂度:这里遍历了这棵树,渐进时间复杂度为 O(n);
  • 空间复杂度:这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n,故渐进空间复杂度为 O(n)。

思路2: 广度优先遍历

function isSymmetric(root) {
  let array = [root, root]
  while (array.length) {
    let left = array.pop()
    let right = array.pop()
    if (left == null && right == null) {
      continue
    }
    if (left == null || right == null) {
      return false
    }
    if (left.val !== right.val) {
      return false
    }
    array.push(left.left, right.right)
    array.push(left.right, right.left)
  }
  return true
}

复杂度分析

  • 时间复杂度:这里遍历了这棵树,渐进时间复杂度为 O(n);
  • 空间复杂度:这里需要用一个队列来维护节点,每个节点最多进队一次,出队一次,队列中最多不会超过 n* 个点,故渐进空间复杂度为 O(n)

四、总结:

涉及到二叉树的问题,可以联想到使用深度优先遍历或者广度优先遍历的方式求解。