Skip to content

Commit a359d1a

Browse files
committed
feat: add #3-4 tasks for trees
1 parent 867f94c commit a359d1a

10 files changed

Lines changed: 508 additions & 137 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ format:
1212
.PHONY: lint
1313
lint:
1414
@echo "Running ruff and mypy..."
15-
@ruff check . --fix
15+
@ruff check . --fix --unsafe-fixes
1616
@mypy src --strict
1717

1818
.PHONY: fix

README.md

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Курс по алгоритмам
1+
# Алгоритмы
22

33
Курс по алгоритмам и структурам данных с разбором теории и решением задач.
44

@@ -11,16 +11,6 @@
1111

1212
## Структура курса
1313

14-
Темы курса разделены на 3 основные части:
15-
16-
* **Базовые алгоритмы**: основы алгоритмов и структур данных, которые нужны для решения самых простых задач.
17-
18-
* **Шаблоны решения**: это типичные подходы к решению задач, которые часто встречаются на собеседованиях.
19-
20-
* **Продвинутые подходы**: это более сложные алгоритмы и структуры данных, которые нужны для решения сложных задач.
21-
22-
Каждая тема содержит теорию, практические задачи и их решения.
23-
2414
|| Тема | Секция | Папка |
2515
|----|----------------------------------|------------------------------|-------------------|
2616
| 1 | Введение в алгоритмы | Базовые алгоритмы | `a_intro` |
@@ -38,6 +28,8 @@
3828
| 13 | Деревья | Продвинутые структуры данных | `m_trees` |
3929
| 14 | Графы | Продвинутые структуры данных | `n_graphs` |
4030

31+
Каждая тема содержит теорию, практические задачи и их решения.
32+
4133
## Источники
4234

4335
* [LeetCode - The World's Leading Online Programming Learning Platform](https://leetcode.com/)

src/m_trees/README.md

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,38 @@
11
# Задачи по деревьям
22

3-
## A. Обход двоичного дерева
3+
## A. Высота дерева
4+
5+
| Поле | Значение |
6+
|-----------|---------------------------------------------------|
7+
| Сложность | Средняя |
8+
| Источник | https://stepik.org/lesson/41234/step/2?unit=19818 |
9+
10+
Дано дерево. Вершины дерева пронумерованы от 0. Требуется определить высоту дерева. Высотой
11+
вершины называется расстояние от неё до самой удалённой вершины. Высотой дерева называется высота корня дерева.
12+
13+
Формат входа. Первая строка содержит натуральное число n. Вторая строка содержит n целых чисел
14+
parent[0], ... , parent[n − 1]. Для каждого 0 ≤ i ≤ n−1, parent[i] — родитель вершины i; если parent[i] = −1,
15+
то i является корнем. Гарантируется, что корень ровно один. Гарантируется, что данная последовательность задаёт
16+
дерево.
17+
18+
Формат выхода. Высота дерева.
19+
20+
Пример.
21+
22+
Вход:
23+
24+
```
25+
10
26+
9 7 5 5 2 9 9 9 2 -1
27+
```
28+
29+
Выход:
30+
31+
```
32+
4
33+
```
34+
35+
## B. Обход двоичного дерева
436

537
| Поле | Значение |
638
|-----------|---------------------------------------------------|
@@ -41,34 +73,68 @@
4173
50 80 90 30 40 70 10 60 20 0
4274
```
4375

44-
## B. Высота дерева
76+
## C. Проверка свойства дерева поиска
4577

4678
| Поле | Значение |
4779
|-----------|---------------------------------------------------|
4880
| Сложность | Средняя |
49-
| Источник | https://stepik.org/lesson/41234/step/2?unit=19818 |
81+
| Источник | https://stepik.org/lesson/45970/step/2?unit=24123 |
5082

51-
Дано дерево. Вершины дерева пронумерованы от 0. Требуется определить высоту дерева. Высотой
52-
вершины называется расстояние от неё до самой удалённой вершины. Высотой дерева называется высота корня дерева.
83+
Дано двоичное дерево. Проверить, является ли оно корректным деревом поиска: верно ли, что для любой вершины дерева её
84+
ключ больше всех ключей в левом поддереве данной вершины и меньше всех ключей в правом поддереве.
5385

54-
Формат входа. Первая строка содержит натуральное число n. Вторая строка содержит n целых чисел
55-
parent[0], ... , parent[n − 1]. Для каждого 0 ≤ i ≤ n−1, parent[i] — родитель вершины i; если parent[i] = −1,
56-
то i является корнем. Гарантируется, что корень ровно один. Гарантируется, что данная последовательность задаёт
57-
дерево.
86+
Формат входа. Первая строка содержит натуральное число n. Следующие n строк содержат информацию о вершинах дерева.
87+
Каждая строка содержит три числа: номер вершины, номер левой дочерней вершины и номер правой дочерней вершины.
88+
Если у вершины нет дочерних вершин, то соответствующее значение равно -1.
5889

59-
Формат выхода. Высота дерева.
90+
Формат выхода. Вывести "CORRECT", если дерево является корректным деревом поиска, и "INCORRECT" в противном случае.
6091

6192
Пример.
6293

63-
Вход:
94+
Ввод:
6495

6596
```
66-
10
67-
9 7 5 5 2 9 9 9 2 -1
97+
3
98+
2 1 2
99+
1 -1 -1
100+
3 -1 -1
68101
```
69102

70-
Выход:
103+
Вывод:
71104

72105
```
73-
4
106+
CORRECT
107+
```
108+
109+
## D. Проверка общего свойства дерева поиска
110+
111+
| Поле | Значение |
112+
|-----------|---------------------------------------------------|
113+
| Сложность | Средняя |
114+
| Источник | https://stepik.org/lesson/45970/step/3?unit=24123 |
115+
116+
Дано двоичное дерево. Проверить, является ли оно корректным деревом поиска **в общем случае**: верно ли, что для любой
117+
вершины дерева её ключ больше всех ключей в левом поддереве и меньше или равен всех ключей в правом поддереве.
118+
119+
Формат входа. Первая строка содержит натуральное число n. Следующие n строк содержат информацию о вершинах дерева.
120+
Каждая строка содержит три числа: номер вершины, номер левой дочерней вершины и номер правой дочерней вершины.
121+
Если у вершины нет дочерних вершин, то соответствующее значение равно -1.
122+
123+
Формат выхода. Вывести "CORRECT", если дерево является корректным деревом поиска, и "INCORRECT" в противном случае.
124+
125+
Пример.
126+
127+
Ввод:
128+
129+
```
130+
3
131+
2 1 2
132+
1 -1 -1
133+
3 -1 -1
134+
```
135+
136+
Вывод:
137+
138+
```
139+
CORRECT
74140
```

src/m_trees/dfs.py

Lines changed: 23 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,32 @@
55

66

77
@dataclass
8-
class Node:
8+
class BNode:
99
key: int
10-
left: Node | None = None
11-
right: Node | None = None
10+
left: BNode | None = None
11+
right: BNode | None = None
1212

1313

1414
# O(n)
1515
def dfs_all(nodes: list[tuple[int, int, int]]) -> tuple[list[int], list[int], list[int]]:
1616
"""
17-
Perform DFS on a tree represented by a list of tuples (key, left_index, right_index).
18-
Returns three lists: inorder, preorder, and postorder traversals.
17+
Обход дерева в глубину (DFS) с использованием рекурсии. Возвращает три списка:
18+
- inorder (симметричный) обход
19+
- preorder (прямой) обход
20+
- postorder (обратный) обход
1921
"""
2022
root = build_tree(nodes)
2123
return (
22-
dfs_inorder(root),
23-
dfs_preorder(root),
24-
dfs_postorder(root),
24+
dfs(root, "inorder"),
25+
dfs(root, "preorder"),
26+
dfs(root, "postorder"),
2527
)
2628

2729

2830
# O(n)
29-
def dfs(root: Node, style: Literal["inorder", "preorder", "postorder"] = "inorder") -> list[int]:
31+
def dfs(root: BNode, style: Literal["inorder", "preorder", "postorder"] = "inorder") -> list[int]:
3032
"""
31-
Perform DFS on a tree and return the traversal in the specified style.
33+
Обход дерева в глубину (DFS) с использованием рекурсии.
3234
"""
3335
match style:
3436
case "inorder":
@@ -42,9 +44,9 @@ def dfs(root: Node, style: Literal["inorder", "preorder", "postorder"] = "inorde
4244

4345

4446
# O(n)
45-
def dfs_inorder(node: Node | None) -> list[int]:
47+
def dfs_inorder(node: BNode | None) -> list[int]:
4648
"""
47-
Perform an inorder DFS traversal of the tree.
49+
Обход дерева в глубину (DFS) в симметричном порядке.
4850
"""
4951
if node is None:
5052
return []
@@ -53,9 +55,9 @@ def dfs_inorder(node: Node | None) -> list[int]:
5355

5456

5557
# O(n)
56-
def dfs_preorder(node: Node | None) -> list[int]:
58+
def dfs_preorder(node: BNode | None) -> list[int]:
5759
"""
58-
Perform a preorder DFS traversal of the tree.
60+
Обход дерева в глубину (DFS) в прямом порядке.
5961
"""
6062
if node is None:
6163
return []
@@ -64,28 +66,26 @@ def dfs_preorder(node: Node | None) -> list[int]:
6466

6567

6668
# O(n)
67-
def dfs_postorder(node: Node | None) -> list[int]:
69+
def dfs_postorder(node: BNode | None) -> list[int]:
6870
"""
69-
Perform a postorder DFS traversal of the tree.
71+
Обход дерева в глубину (DFS) в обратном порядке.
7072
"""
7173
if node is None:
7274
return []
7375
# dfs_postorder(node.left) + dfs_postorder(node.right) + [node.key]
74-
return dfs_postorder(node.left) + dfs_postorder(node.right) + [node.key]
76+
return [*dfs_postorder(node.left), *dfs_postorder(node.right), node.key]
7577

7678

7779
# O(n)
78-
def build_tree(nodes: list[tuple[int, int, int]]) -> Node:
80+
def build_tree(nodes: list[tuple[int, int, int]]) -> BNode:
7981
"""
80-
Build a binary tree from a list of tuples where each tuple contains:
81-
- key: the value of the node
82-
- left_index: index of the left child (-1 if no left child)
83-
- right_index: index of the right child (-1 if no right child)
82+
Строит бинарное дерево из списка кортежей (ключ, индекс левого ребенка, индекс правого ребенка).
83+
-1 используется для обозначения отсутствующего ребенка.
8484
"""
8585
n = len(nodes)
8686
if n <= 0:
8787
raise ValueError("Input list must contain at least one element.")
88-
tree = [Node(0) for _ in range(n)]
88+
tree = [BNode(0) for _ in range(n)]
8989
for i in range(n):
9090
key, left, right = nodes[i]
9191
tree[i].key = key
@@ -94,80 +94,3 @@ def build_tree(nodes: list[tuple[int, int, int]]) -> Node:
9494
if right >= 0:
9595
tree[i].right = tree[right]
9696
return tree[0]
97-
98-
99-
# Advanced: Iterative DFS implementation
100-
101-
102-
# O(n)
103-
def dfs_all_iterative(nodes: list[tuple[int, int, int]]) -> tuple[list[int], list[int], list[int]]:
104-
"""
105-
Perform an iterative DFS on a tree represented by a list of tuples (key, left_index, right_index).
106-
Returns three lists: inorder, preorder, and postorder traversals.
107-
"""
108-
root = build_tree(nodes)
109-
return (
110-
dfs_inorder_iterative(root),
111-
dfs_preorder_iterative(root),
112-
dfs_postorder_iterative(root),
113-
)
114-
115-
116-
# O(n)
117-
def dfs_preorder_iterative(node: Node) -> list[int]:
118-
"""
119-
Perform an iterative preorder DFS traversal of the tree.
120-
"""
121-
st = [node]
122-
result = []
123-
while st:
124-
node = st.pop()
125-
result.append(node.key)
126-
# Push right first so that left is processed first
127-
if node.right is not None:
128-
st.append(node.right)
129-
if node.left is not None:
130-
st.append(node.left)
131-
return result
132-
133-
134-
# O(n)
135-
def dfs_postorder_iterative(node: Node) -> list[int]:
136-
"""
137-
Perform an iterative postorder DFS traversal of the tree.
138-
"""
139-
st = [node]
140-
result = []
141-
while st:
142-
node = st.pop()
143-
result.append(node.key)
144-
# Push left first so that right is processed first
145-
if node.left is not None:
146-
st.append(node.left)
147-
if node.right is not None:
148-
st.append(node.right)
149-
# Reverse the result to get postorder
150-
result.reverse()
151-
return result
152-
153-
154-
# O(n)
155-
def dfs_inorder_iterative(node: Node) -> list[int]:
156-
"""
157-
Perform an iterative inorder DFS traversal of the tree.
158-
"""
159-
st: list[Node] = []
160-
result: list[int] = []
161-
curr: Node | None = node
162-
while st or curr is not None:
163-
# Go to the leftmost node
164-
if curr is not None:
165-
st.append(curr)
166-
curr = curr.left
167-
# Process the node
168-
else:
169-
# Pop from stack and visit the node
170-
curr = st.pop()
171-
result.append(curr.key)
172-
curr = curr.right
173-
return result

0 commit comments

Comments
 (0)