Обход дерева — систематический способ посещения всех вершин дерева. Существует несколько основных способов обхода бинарного дерева, каждый из которых имеет свои применения.
Виды обходов
Основные виды обхода бинарного дерева:
Название
Порядок посещения
Применение
Прямой (preorder)
Корень → Лево → Право
Копирование дерева, префиксные выражения
Центрированный (inorder)
Лево → Корень → Право
Сортировка, вывод BST по возрастанию
Обратный (postorder)
Лево → Право → Корень
Удаление дерева, постфиксные выражения
В ширину (level-order)
По уровням сверху вниз
Поиск в ширину, вывод по уровням
Сравнение обходов на одном дереве
Рассмотрим дерево:
graph TD
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
style A fill:#D6E4F0,stroke:#004E8C
style B fill:#D6E4F0,stroke:#004E8C
style C fill:#D6E4F0,stroke:#004E8C
style D fill:#E8F8F5,stroke:#27AE60
style E fill:#E8F8F5,stroke:#27AE60
Обход
Порядок
Результат
Preorder
Корень → Лево → Право
1, 2, 4, 5, 3
Inorder
Лево → Корень → Право
4, 2, 5, 1, 3
Postorder
Лево → Право → Корень
4, 5, 2, 3, 1
Level-order
По уровням
1, 2, 3, 4, 5
Прямой обход (Preorder)
При прямом обходе сначала посещается корень, затем рекурсивно обходится левое поддерево, затем правое.
defpreorder_iterative(root):
"""Итеративный прямой обход с использованием стека"""if root isNone:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.value)
# Правого ребёнка добавляем первым (LIFO)if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
definorder_iterative(root):
"""Итеративный центрированный обход""" result = []
stack = []
current = root
while current or stack:
# Спускаемся к самому левому узлуwhile current:
stack.append(current)
current = current.left
# Обрабатываем узел current = stack.pop()
result.append(current.value)
# Переходим к правому поддереву current = current.right
return result
#include<vector>#include<stack>std::vector<int> inorderIterative(TreeNode* root) {
std::vector<int> result;
std::stack<TreeNode*> stack;
TreeNode* current = root;
while (current ||!stack.empty()) {
// Спускаемся к самому левому узлу
while (current) {
stack.push(current);
current = current->left;
}
// Обрабатываем узел
current = stack.top();
stack.pop();
result.push_back(current->value);
// Переходим к правому поддереву
current = current->right;
}
return result;
}
Обратный обход (Postorder)
При обратном обходе сначала рекурсивно обходится левое поддерево, затем правое, затем посещается корень.
Рекурсивная реализация
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defpostorder_recursive(root, result=None):
"""Обратный обход: лево → право → корень"""if result isNone:
result = []
if root isNone:
return result
postorder_recursive(root.left, result) # 1. Обойти левое поддерево postorder_recursive(root.right, result) # 2. Обойти правое поддерево result.append(root.value) # 3. Посетить кореньreturn result
# Для дерева из примера выше:print(postorder_recursive(root)) # [4, 5, 2, 3, 1]
defpostorder_iterative(root):
"""Итеративный обратный обход"""if root isNone:
return []
result = []
stack = [root]
prev =Nonewhile stack:
current = stack[-1]
# Спускаемся внизif prev isNoneor prev.left == current or prev.right == current:
if current.left:
stack.append(current.left)
elif current.right:
stack.append(current.right)
# Поднимаемся от левого ребёнкаelif current.left == prev:
if current.right:
stack.append(current.right)
# Поднимаемся от правого ребёнка или это листelse:
result.append(current.value)
stack.pop()
prev = current
return result
defpostorder_iterative_simple(root):
"""Упрощённая версия через модифицированный preorder"""if root isNone:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.value)
# Обратный порядок: левый, потом правыйif node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return result[::-1] # Разворачиваем результат
from collections import deque
deflevel_order(root):
"""Обход в ширину с использованием очереди"""if root isNone:
return []
result = []
queue = deque([root])
while queue:
node = queue.popleft()
result.append(node.value)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
deflevel_order_by_levels(root):
"""Обход в ширину с группировкой по уровням"""if root isNone:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.value)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
# Для дерева из примера выше:print(level_order(root)) # [1, 2, 3, 4, 5]print(level_order_by_levels(root)) # [[1], [2, 3], [4, 5]]
Для корректного удаления дерева нужно сначала удалить детей, затем родителя — используется postorder обход.
1
2
3
4
5
6
7
8
9
10
11
defdelete_tree(root):
"""Удаление дерева с освобождением памяти"""if root isNone:
return delete_tree(root.left)
delete_tree(root.right)
# В Python сборщик мусора освободит память автоматически# В явном удалении нет необходимости, но для примера: root.left =None root.right =None