题目难度: 简单
原题链接
今天继续更新剑指 offer 系列, 老样子晚上 6 点 45 分准时更新公众号 每日精选算法题, 大家记得关注哦~ 另外在公众号里回复 offer 就能看到剑指 offer 系列当前连载的所有文章了
本篇是剑指 offer 系列的最后一篇了, 之后我会做一个总结, 对之前的文章进行归类汇总, 希望这个系列对大家能有所帮助, 也欢迎大家多多分享和转发, 谢谢支持~
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
说明:
示例 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。因为根据定义最近公共祖先节点可以为节点本身。
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode,
q: TreeNode) -> TreeNode:
# 方法1: 递归dfs, 返回是否找到p或q
# 注意ancestor只能赋值一次, 即为最近的祖先
ancestor = None
def find(cur):
nonlocal ancestor
if not cur or ancestor:
# 递归出口, 返回全false
return (False, False)
# 先得出左右子树上的p/q节点存在情况
lp, pq = find(cur.left)
rp, rq = find(cur.right)
# 然后计算加上当前节点后的p/q节点存在情况
findp = lp or rp or cur == p
findq = pq or rq or cur == q
# 如果两者都存在, 且祖先还为空, 则表示找到最近祖先了, 即当前节点
if findp and findq and not ancestor:
ancestor = cur
# 返回当前节点以及其所有子节点中的p/q节点存在情况
return (findp, findq)
find(root)
return ancestor
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode,
q: TreeNode) -> TreeNode:
# 方法2: 迭代BFS, 记录每个节点父节点, p/q依次向上找并加入集合中, 直到找到第一个父节点已在集合的节点, 这个父节点就是最近公共祖先
if not root:
return None
# 初始化根节点的父节点为空
parent = {root: None}
# BFS部分, 记录每个节点父节点
queue = [root]
for node in queue:
if node.left:
parent[node.left] = node
queue.append(node.left)
if node.right:
parent[node.right] = node
queue.append(node.right)
# 注意集合需要初始化加上p/q, 因为它们可能自身就是祖先
v = {p, q}
while p or q:
# 这里循环条件为or, 因为有可能p/q高度可能不同, 有可能某个节点先遍历到了根节点, 这时候另外的节点需要继续往上走来找祖先
if p:
p = parent[p]
if p and p in v:
# 当前父节点不是空且已经在集合中, 说明它就是最近祖先
return p
v.add(p)
if q:
q = parent[q]
if q and q in v:
# 当前父节点不是空且已经在集合中, 说明它就是最近祖先
return q
v.add(q)
# 最终一定会在循环内部返回的, 所以循环外不需要return, 因为保底祖先是根节点, 一定在集合中
大家可以在下面这些地方找到我~
我的知乎专栏
我的头条号
我的 CSDN
我的 Leetcode
我的牛客网博客
我的公众号: 每日精选算法题, 欢迎大家扫码关注~