Изучение различных методов поиска наименьшего общего предка в двоичном дереве

В этой статье блога мы рассмотрим различные методы поиска наименьшего общего предка (LCA) в двоичном дереве. LCA — это общий предок двух узлов, расположенный дальше всего от корня. Мы рассмотрим несколько подходов и предоставим примеры кода для иллюстрации каждого метода. Итак, начнем!

Метод 1: рекурсивное решение
Один из наиболее простых подходов к поиску LCA в двоичном дереве — использование рекурсивного алгоритма. Вот пример реализации на Python:

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
def find_lca(root, p, q):
    if root is None:
        return None
    if root == p or root == q:
        return root
    left_lca = find_lca(root.left, p, q)
    right_lca = find_lca(root.right, p, q)
    if left_lca and right_lca:
        return root
    if left_lca:
        return left_lca
    if right_lca:
        return right_lca
    return None

Метод 2. Использование родительских указателей.
Другой подход заключается в использовании родительских указателей при обходе дерева. Этот метод требует дополнительного места для хранения родительских указателей каждого узла. Вот пример реализации на Python:

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.parent = None
def populate_parent_pointers(root, parent=None):
    if root is None:
        return
    root.parent = parent
    populate_parent_pointers(root.left, root)
    populate_parent_pointers(root.right, root)
def find_lca_with_parent_pointers(p, q):
    ancestors = set()
    while p:
        ancestors.add(p)
        p = p.parent
    while q:
        if q in ancestors:
            return q
        q = q.parent
    return None

Метод 3: использование метода двоичного подъема
Двоичный подъем — это усовершенствованный метод, который оптимизирует вычисление LCA для нескольких запросов в дереве. Он предварительно обрабатывает дерево и сохраняет информацию, которая позволяет проводить эффективные расчеты LCA. Вот пример реализации на Python:

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
def preprocess_tree(root):
    MAX_N = 1000  # Maximum number of nodes in the tree
    MAX_LOGN = 10  # Maximum logarithmic depth of the tree
    parent = [[0] * MAX_LOGN for _ in range(MAX_N)]
    depth = [0] * MAX_N
    def dfs(node, par=None):
        parent[node][0] = par
        if par is not None:
            depth[node] = depth[par] + 1
        for child in [node.left, node.right]:
            if child:
                dfs(child, node)
    dfs(root)
    for j in range(1, MAX_LOGN):
        for i in range(MAX_N):
            if parent[i][j - 1] != -1:
                parent[i][j] = parent[parent[i][j - 1]][j - 1]
    return parent, depth
def find_lca_with_binary_lifting(p, q, parent, depth):
    MAX_LOGN = 10  # Maximum logarithmic depth of the tree
    if depth[p] < depth[q]:
        p, q = q, p
    for i in range(MAX_LOGN - 1, -1, -1):
        if depth[p] - (1 << i) >= depth[q]:
            p = parent[p][i]
    if p == q:
        return p
    for i in range(MAX_LOGN - 1, -1, -1):
        if parent[p][i] != parent[q][i]:
            p = parent[p][i]
            q = parent[q][i]
    return parent[p][0]

В этой статье мы рассмотрели несколько методов поиска наименьшего общего предка (LCA) в двоичном дереве. Мы рассмотрели рекурсивные решения с использованием родительских указателей и технику двоичного подъема. Каждый метод имеет свои преимущества и может подойти в зависимости от конкретных требований вашего приложения. Понимая эти различные подходы, вы сможете выбрать наиболее подходящий для ваших нужд.