题目链接
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/
题目描述
给定一个二叉树,找到该树中两个指定节点p、q的最近公共祖先。
树中所有节点的值都唯一,p、q为不同节点且均存在于给定的二叉树中。
示例
输入:root = [3,5,1,6,2,0,8,null,null,7,4],p=5,q = 1
输出:3
节点1和节点5的最近公共祖先是节点3。
解题思路
对于二叉树问题,逃不开二叉树的前序、中序、后序遍历这三种递归顺序。对于这道题,两个指定节点p和q的最近公共祖先nearest有三种情况:
(1)p在nearest的左子树中,q在nearest的右子树中。或者p在nearest的右子树中,q在nearest的左子树中。
(2)q在p的子树中,此时nearest = p。
(3)p在q的子树中,此时nearest = q。
因此我们很容易想到,要先不断向下递归在二叉树中找到p和q,再向上回溯找p和q的最近公共祖先。因此我们设计一个递归函数,这个递归函数的参数为(root,p,q):
(1)如果以root为根节点的子树中既有p,又有q,就返回p和q的最近公共祖先。
(2)如果以root为根节点的子树中只有p,那么就返回p;如果以root为根节点的子树中只有q,那么就返回q。(如果以root为根节点的子树中只有p或q一个元素,那么就返回p或q的最近祖先,即p或q本身。)
(3)如果以root为根节点的子树中既没有p也没有q,此时返回None。(相当于剪枝)
首先我们要定义递归的终点:如果root为None,说明递归到了最后一层,此时返回root(None)即可;如果root为p,此时就返回p。如果root为q,就返回q。
由于我们的搜索方向是先自上而下找p和q,每次递归搜索root左子树和root右子树,然后再往上回溯判断当前节点是否是p和q的最近公共祖先(根据左子树和右子树的递归结果判断)。因此我们要采用后序遍历的顺序。
举例说明算法步骤:
Python实现
在下面的代码中,如果p和q分别位于root的两个子树中(比如p在root的左子树中,q在root的右子树中),那么left就等于p(递归到root == p的时候将p不断向上返回),right就等于q(递归到root ==q时将q不断向上返回)。我们将root返回,因为在后序遍历+回溯过程中第一个满足left==p且right==q条件的节点一定是最近公共祖先。
如果p和q都不存在root的两个子树中,此时left == None,right== None,最后返回None。
如果p和q都只存在于root的左子树中,此时如果p比q高,那么下一层递归返回的left为p,right为None(递归找到p就会返回,不会再往下找q。因为如果q在p的子树中,其他子树如当前层的root.right一定不含有q,只会返回None)。当前层递归返回left。
如果p和q都只存在于root的右子树中,此时如果p比q高,那么下一层递归返回的right为p,left为None。当前层递归返回right。
如果root的子树中只存在p,假设p存在于root的左子树中,右子树中不存在q(由于节点唯一,已知p在root的左子树中,p一定不会出现在root的右子树中),那么left就等于p,right等于None,当前层的递归就返回left。
root的子树中只存在q是类似的情况。
def lowestCommonAncester(self,root,p,q):
#递归终止条件判断:root为None时、root为p时、root为q时
if(root == None or root == p or root == q):
return root
#递归左子树,找p、q的最近公共祖先(如果存在),或p的最近祖先(如果左子树中只存在p,则返回p)或q的最近祖先(如果左子树中只存在q,则返回q)
left = self.lowestCommonAncester(root.left,p,q)
#递归右子树,找p、q的最近公共祖先(如果存在),或p的最近祖先(如果右子树中只存在p,则返回p)或q的最近祖先(如果右子树中只存在q,则返回q)
right = self.lowestCommonAncester(root.right,p,q)
#在这里对递归返回的left和right做处理
#如果left不为空,right不为空,说明p和q分别处于root的两个子树中,由于是后序遍历,第一个满足这个条件的root就是最近公共祖先,我们把这个节点往回传。
if(left and right):
return root
#如果right为空,left不为空。说明root的右子树中既没有p也没有q,左子树中可能既有p也有q,可能只有p,可能只有q。left有可能是最近公共祖先
if not right and left:
return left
if not left and right:
return right