本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
原题链接 👉 236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入:root = [1,2], p = 1, q = 2
输出:1
注意:
- 树中节点数目在范围 [2, 105] 内。
- -109 <= Node.val <= 109
- 所有 Node.val 互不相同 。
- p != q
- p 和 q 均存在于给定的二叉树中。
二、思路分析:
**思路1:**递归
本题是求两个子节点的最近公共祖先节点。涉及到树的问题就先想到深度优先遍历和广度优先遍历。只要题目中指明是按照层级来处理的,就先考虑用广度优先遍历;其他场景特别是要转化成左右子树的场景可以考虑用深度优先遍历。此题用深度遍历。
-
确定递归终止条件:
if (!root || root == p || root == q) return root
如果root为空,返回root,不走下面的逻辑;
root == p
或者root == q
表明当前节点root
就是父节点,返回即可。 -
递归root.left和root.right,看这两个子树下是否有p,q的公共父节点。
let left = lowestCommonAncestor(root.left, p, q) let right = lowestCommonAncestor(root.right, p, q)
- 如果left和right都是null,说明p,q两个节点既不在左子树下,也不在右子树下,返回null即可
- 如果left为null,right不为null,说明p,q两个节点在右子树下
- 如果right为null,left不为null,说明p,q两个节点在左子树下
- 如果left和right都不是null,说明root就是父节点
三、完整代码:
思路1:
var lowestCommonAncestor = function (root, p, q) {
if (!root || root == p || root == q) return root
let left = lowestCommonAncestor(root.left, p, q)
let right = lowestCommonAncestor(root.right, p, q)
if (left == null && right == null) return null
if (left == null) return right
if (right == null) return left
return root
}
这道题还可以进行进一步的变形,是求公共祖先节点的值。只需要在上代码中小做修改就行。大家可以思考下。
复杂度分析:
- 时间复杂度:O(N),其中 N 是二叉树的节点数。二叉树的所有节点有且只会被访问一次,因此时间复杂度为 O(N)。
- 空间复杂度:O(N) ,其中 N 是二叉树的节点数。递归调用的栈深度取决于二叉树的高度,二叉树最坏情况下为一条链,此时高度为 N,因此空间复杂度为 O(N)。
四、总结:
二叉树的问题首先要想到深度优先遍历或广度优先遍历。查找二叉树的公共祖先节点使用到的是深度优先遍历。