Python面试宝典第43题:二叉树的最近公共祖先

135 阅读3分钟

题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

最近公共祖先的定义为:对于有根树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。递归法的实现简单直观,但对于极度不平衡的二叉树,递归深度可能非常深,有出现栈溢出的风险。