Есть такое понятие «level order traversal of binary tree». Это ничто иное, как поуровневый обход ( вывод ) двоичного дерева. Периодически на практике я сталкиваюсь с такой задачей, поэтому решил сделать шпаргалку-заметку, как это реализовать на языке программирования. Также расскажу более подробно об этом алгоритме.

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

классическое двоичное дерево поиска ( ключи узлов - натуральные числа )

Рисунок — классическое двоичное дерево поиска ( ключи узлов — натуральные числа )

Давайте сразу договоримся, что корень дерева ( узел с ключом $100$ ) лежит на уровне $\#1$. В теории алгоритмов многие считают корневой уровень равный $0$. В принципе, это абсолютно не важно, но я выбирают счет от $1$!

Сейчас я приведу картинку с тем же самым двоичным деревом, но его узлы, принадлежащие разным уровням, будут закрашены в разные цвета.

бинарное дерево с поуровневой раскраской узлов

Рисунок — бинарное дерево с поуровневой раскраской узлов

Поуровневый обход такого дерева должен давать примерно такие результаты ( например, вывод на экран пользователю ):

Уровень Ключи Цвет
$1$ $100$ синий
$2$ $50,\ 150$ оранжевый
$3$ $25,\ 67,\ 125$ зеленый
$4$ $58,\ 83$ коричневый

➡ Вспомогательная операция — когда по запросу пользователя выводятся узлы заданного уровня. Но это реализуется достаточно тривиально, если есть готовая функция горизонтального обхода бинарного дерева.

Сейчас надо подумать, насколько сложно реализовать поуровневый обход двоичного дерева. В одной из своих заметок-шпаргалок я показывал, какие бывают классические типы обходов бинарного дерева. Всего было продемонстрировано $6$ рекурсивных обходов, причем все они работали «на глубину», то есть при обходе дерева алгоритм стремился спускаться по ветвям. При горизонтальном обходе узлов дерева нужен другой подход!

💡 Думаю, что придется отказаться от рекурсивной функции, которая посещает все вершины двоичного дерева по горизонтальным слоям, и воспользоваться итеративным подходом.

Тем, кто знаком с теорией графов, наверняка приходит на ум алгоритм BFS ( Breadth-first-search, обход в ширину ). Да, это верная идея. Именно этот алгоритм я попробую адаптировать для послойного обхода узлов двоичного дерева.

➡ Как известно, алгоритм BFS задействуют такую популярную структуру данных, как очередь. Поэтому для начала мне придется запрограммировать все базовые операции над очередью.

Для начала задекларирую типы данных, описывающих элементы очереди, а также саму очередь:

А вот прототипы функций, для работы с очередью и ее элементами:

Приводить полный код функций для работы с очередью не буду ( оставляю вам на откуп их несложную реализацию ), но словесно поясню назначение каждой функции:

# Функция Назначение
$1$ void Init( Queue* const ); Инициализация очереди перед началом работы.
$2$ Elem* Create_elem( Node* const ); Создание элемента очереди перед тем, как он будет добавлен в конец очереди.
$3$ int Empty( const Queue* const ); Проверка очереди на пустоту ( 0 — непустая; 1 — пустая ).
$4$ Elem* Front( const Queue* const ); Получение указателя на первый элемент очереди. При этом сам элемент из очереди не удаляется.
$5$ void Pop( Queue* const ); Физическое удаление элемента из конца очереди.
$6$ void Push( Queue* const, Node* const ); Добавление нового элемента в конец очереди.

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

➡ Очень важно понимать, что элементом очереди является указатель на вершину двоичного дерева ( это поле Node* node в структуре Elem ).

На старте алгоритма в первую очередь помещается элемент, указывающий на корень дерева, а вторая очередь является пустой:

старт алгоритма горизонтального обхода двоичного дерева

Рисунок — старт алгоритма горизонтального обхода двоичного дерева

Затем в цикле получаем все элементы из очереди #1 и в очередь #2 добавляем соответствующие вершины-сыновья полученных элементов:

добавление вершин-сыновей в очередь #2

Рисунок — добавление вершин-сыновей в очередь #2

Обработанные элементы очереди #1 удаляются из нее и, когда очередь #1 становится пустой, то происходит замещение очереди #1 очередью #2:

замещение очередей

Рисунок — очередь #2 замещает очередь #1

➡ Этот процесс продолжается до тех пор, пока обе очереди не станут пустыми.

Очевидно, что в процессе работы алгоритма происходит также выпечатывание на экран ( или файл ) значения ключей вершин двоичного дерева.

Моя программа для представленного выше двоичного дерева ( поиска ) выдает следующие результаты:

результаты работы программы

Рисунок — результаты работы программы

Также хочу прогнать функцию для более «ветвистого» двоичного дерева ( поиска ), например, для такого:

более "ветвистое" двоичное дерево ( поиска ) для теста

Рисунок — более «ветвистое» двоичное дерево ( поиска ) для теста

Результаты получились следующими:

результаты работы программы для "ветвистого" двоичного дерева ( поиска )

Рисунок — результаты работы программы для «ветвистого» двоичного дерева ( поиска )

💡 Хочу отметить такой момент: в своей программной реализации для послойного обхода вершин двоичного дерева я задействовал $2$ очереди:

первая очередь отвечала за вершины бинарного дерева, находящиеся на «текущем» уровне;
вторая ( вспомогательная ) очередь отвечала за вершины бинарного дерева, находящиеся на «следующем» уровне.

Зачастую проуровневый обход дерева кодируют с применением лишь одной очереди. Но это дело вкуса. Мне удобнее было задействовать две очереди.

Также существуют рекурсивные алгоритмы горизонтального обхода узлов бинарного дерева ( поиска ), но они сложны для понимания и их крайне неудобно программировать.

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

ВНИМАНИЕ Для заказа программы на двоичное дерево ( поиска ) пишите на мой электронный адрес proglabs@mail.ru