题目如下
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
_______3______ / \ ___5__ ___1__ / \ / \ 6 _2 0 8 / \ 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。
因为根据定义最近公共祖先节点可以为节点本身。说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉树中。
这个题已经有相当巧妙的解法(递归判断节点的父子关系),我当时做的时候剑走偏锋想了个别的方法,答案里没有看到这个思路,所以简单记录。
一开始看到这个题的反应是,如果我不想重复地去遍历某些节点,那么在遍历过程中就需要把已经遍历到的内容记录下来以备后查。受到高级算法链表模块中【复制带随机指针地链表】答案的启发,可以用字典来存储已知父子节点对应关系,当然我们是想要从子节点获得父节点的,就可以将子节点、父节点分别记作字典的key和value。当目标节点p、q都遍历到时,我们不关心更深层次节点的构造,可以直接跳出。
current = [root]
dic = dict()
while current:
childs = []
for node in current:
if node:
left,right = node.left,node.right
dic[left] = node
dic[right] = node
if dic.get(p) and dic.get(q):
break
childs.append(left)
childs.append(right)
current = childs
这样的话问题就变成了如何根据这个字典获取公共父节点。从字典中依次回溯,将内容存入列表,找两个列表的第一个公共值当然是可行的一个思路,但是如果树的深度较大,平方复杂度可能造成无法接受的运行时间。其实字典的key-value结构和链表是有些相似的,因此就想到了中级算法中【链表交叉】一题。
编写一个程序,找到两个单链表相交的起始节点。
例如,下面的两个链表:
A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3在节点 c1 开始相交。
当时解决这个问题的算法是这样的
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
#两个指针,一个从A遍历到B,一个从B遍历到A,因为A+B和B+A的长度一定是相等的,所以两个指针一定会在相交节点处相遇
cursor1 = headA
cursor2 = headB
while cursor1 != cursor2:
#如果都不相等,第二遍循环到头的时候总会一起返回null
cursor1 = cursor1.next if cursor1 else headB
cursor2 = cursor2.next if cursor2 else headA
return cursor1
这里除了链表换成了字典,其他的问题是一模一样的,代码也可以直接拿过来用,只要把next改为dic即可。完整版代码如下:
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root:
return None
if root in [p,q]:
return root
current = [root]
dic = dict()
while current:
childs = []
for node in current:
if node:
left,right = node.left,node.right
dic[left] = node
dic[right] = node
if dic.get(p) and dic.get(q):
break
childs.append(left)
childs.append(right)
current = childs
cursor1 = p
cursor2 = q
while cursor1 != cursor2:
cursor1 = dic[cursor1] if dic.get(cursor1) else q
cursor2 = dic[cursor2] if dic.get(cursor2) else p
return cursor1
运行结果: