本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
原题链接 👉 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 的左节点
根据上面信息可以总结出递归函数的两个条件:
- 终止条件为:left 和 right 不等,或者 left 和 right 都为空
- 递归的比较 left,left 和 right.right,递归比较 left,right 和 right.left
思路2: 广度优先遍历
- 引入一个队列,这是把递归程序改写成迭代程序的常用方法。
- 初始化时我们把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像)
- 然后将两个结点的左右子结点按相反的顺序插入队列中。
- 当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束。
三、完整代码:
思路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)
四、总结:
涉及到二叉树的问题,可以联想到使用深度优先遍历或者广度优先遍历的方式求解。