Разбор задачи: Заполнение указателей следующего права в каждом узле

Предложенный нами код решения:

 def connect(root):
    """
    Соединяет указатели `next` каждого узла в идеально сбалансированном бинарном дереве.
    :param root: Корень дерева типа Node
    :return: Корень дерева с установленными указателями `next`
    """
    if not root:
        return None

    # Начинаем с верхнего уровня дерева
    leftmost = root

    while leftmost.left:
        head = leftmost

        while head:
            # Соединяем левый ребенок с правым ребенком
            head.left.next = head.right

            # Соединяем правый ребенок с левым ребенком соседнего узла (если есть)
            if head.next:
                head.right.next = head.next.left

            # Переходим к следующему узлу на текущем уровне
            head = head.next

        # Спускаемся на уровень ниже
        leftmost = leftmost.left

    return root

Разбор решения задачи

Функция connect используется для заполнения указателей next в идеально сбалансированном бинарном дереве. Рассмотрим решение шаг за шагом, включая текстовое описание и разбор ключевых фрагментов кода.


1. Проверка на пустое дерево

if not root:
    return None

Этот фрагмент проверяет, является ли входное дерево пустым. Если корень дерева равен None, функция сразу возвращает None, так как заполнять указатели next нечему.


2. Инициализация переменной для работы с уровнями

leftmost = root

Здесь переменная leftmost используется для отслеживания самого левого узла текущего уровня. Мы начинаем с корня дерева, поскольку он находится на первом уровне.


3. Внешний цикл для перехода между уровнями

while leftmost.left:

Этот цикл работает, пока существует следующий уровень, на который можно спуститься. Поскольку дерево идеально сбалансировано, проверка leftmost.left гарантирует, что мы будем обрабатывать только уровни с узлами.


4. Внутренний цикл для работы с узлами на текущем уровне

head = leftmost
while head:
    ...
    head = head.next

На каждом уровне мы начинаем с самого левого узла (head = leftmost) и последовательно переходим к следующему узлу на этом же уровне с помощью указателя next.


5. Соединение левого и правого ребенка

head.left.next = head.right

Здесь происходит ключевая операция: левый ребенок текущего узла соединяется с его правым ребенком через указатель next.

Пример:
Для узла со значением 2:

  • head.left — это левый ребенок, например, 4.
  • head.right — это правый ребенок, например, 5.
    В результате head.left.next будет указывать на head.right.

6. Соединение правого ребенка с левым ребенком соседнего узла

if head.next:
    head.right.next = head.next.left

Если у текущего узла (head) есть сосед справа (head.next), то правый ребенок текущего узла соединяется с левым ребенком соседнего узла.

Пример:
Для узла со значением 2 и его соседа 3:

  • head.right — это правый ребенок узла 2, например, 5.
  • head.next.left — это левый ребенок узла 3, например, 6.
    В результате head.right.next будет указывать на head.next.left.

7. Переход к следующему узлу на уровне

head = head.next

Этот шаг перемещает указатель head к следующему узлу на текущем уровне.


8. Спуск на уровень ниже

leftmost = leftmost.left

После завершения обработки всех узлов текущего уровня, мы перемещаемся на уровень ниже, начиная с самого левого узла на этом уровне.


Итог

Функция обрабатывает дерево уровнями, начиная с корня и спускаясь вниз. На каждом уровне:

  1. Левый ребенок соединяется с правым.
  2. Правый ребенок соединяется с левым ребенком соседнего узла (если сосед есть).

Пример работы функции

Дерево до обработки:

        1
       / \
      2   3
     / \ / \
    4  5 6  7
  • Уровень 1: узел 1 (нет соседей, next = None).
  • Уровень 2: узел 2 соединяется с узлом 3 (2.next = 3).
  • Уровень 3: узлы 4 -> 5 -> 6 -> 7 соединяются слева направо.

Дерево после обработки:

        1 -> None
       / \
      2 -> 3 -> None
     / \ / \
    4->5->6->7 -> None

 



Вы должны Войти или Зарегистрироваться чтобы оставлять комментарии