题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:对于有根树T的两个节点p、q,最近公共祖先表示为一个节点x,满足x是p、q的祖先且x的深度尽可能大。注意:一个节点也可以是它自己的祖先。
备注:
(1)所有节点的值互不相同。
(2)p != q,且p和q均存在于给定的二叉树中。
示例 1:
输入:root = [3,5,1,6,2,0,8,None,None,7,4], p = 5, q = 1
输出:3
解释:节点5和节点1的最近公共祖先是节点3。
示例 2:
输入:root = [3,5,1,6,2,0,8,None,None,7,4], p = 5, q = 4
输出:5
解释:节点5和节点4的最近公共祖先是节点5,因为根据定义,最近公共祖先节点可以为节点本身。
递归法
递归法的基本思想是:通过递归地遍历二叉树,直到找到p或q中的一个节点为止;如果左右子树都返回了非空节点,则说明当前节点就是最近公共祖先;如果只有一个子树返回了非空节点,则返回该非空节点继续向上层传递。使用递归法求解本题的主要步骤如下。
1、如果当前节点为空,或者当前节点就是p或q中的一个,则直接返回当前节点。
2、递归地在左子树中查找p和q的最近公共祖先。
3、递归地在右子树中查找p和q的最近公共祖先。
4、如果左右子树都返回了非空节点,则当前节点就是最近公共祖先。
5、如果只有左子树或右子树返回了非空节点,则返回该非空节点。
根据上面的算法步骤,我们可以得出下面的示例代码。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
# 构建二叉树
def build_binary_tree(values):
if not values:
return None
root = TreeNode(values[0])
queue = [root]
front = 0
index = 1
while index < len(values):
node = queue[front]
front += 1
item = values[index]
index += 1
if item is not None:
left_number = item
node.left = TreeNode(left_number)
queue.append(node.left)
if index >= len(values):
break
item = values[index]
index += 1
if item is not None:
right_number = item
node.right = TreeNode(right_number)
queue.append(node.right)
return root
def lowest_common_ancestor_by_recursion(root, p, q):
if not root or root == p or root == q:
return root
left = lowest_common_ancestor_by_recursion(root.left, p, q)
right = lowest_common_ancestor_by_recursion(root.right, p, q)
if left and right:
return root
else:
return left or right
values = [3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]
root = build_binary_tree(values)
p = root.left
q = root.right
print(lowest_common_ancestor_by_recursion(root, p, q).val)
p = root.left
q = root.left.right.right
print(lowest_common_ancestor_by_recursion(root, p, q).val)
总结
递归法需要遍历整个二叉树,故时间复杂度为O(n),其中n是二叉树的节点数量。其空间复杂度为O(h),其中h是二叉树的高度,这也是递归调用栈的深度。对于平衡二叉树,h = log(n)。对于极度不平衡的二叉树,h = n。递归法的实现简单直观,但对于极度不平衡的二叉树,递归深度可能非常深,有出现栈溢出的风险。