-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eae87f8
commit 9038f0e
Showing
16 changed files
with
294 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Деревья | ||
|
||
## Деревья | ||
|
||
**Дерево** - это иерархическая структура данных, состоящая из вершин и ребер, которые их соединяют. | ||
|
||
Деревья подобны графам, однако, между ними есть несколько ключевых отличий: | ||
|
||
* Иерархия. Деревья имеют строгую иерархию (корень, ветви, листья). Графы не имеют явной иерархии. | ||
* Связи. В деревьях каждая вершина (кроме корня) имеет ровно одного родителя. В графах вершина может быть связана с | ||
любым количеством других вершин. | ||
* Циклы. Деревья не содержат циклов. Графы могут содержать циклы. | ||
|
||
Термины, которые используются при работе с деревьями: | ||
|
||
| Понятие | Описание | | ||
|-----------------|--------------------------------------------------------| | ||
| Вершина | Элемент дерева, который содержит данные. | | ||
| Ребро | Связь между двумя вершинами. | | ||
| Корень | Вершина, которая не имеет родителя. | | ||
| Листья | Вершины, которые не имеют дочерних вершин. | | ||
| Путь | Последовательность вершин, соединенных ребрами. | | ||
| Высота вершины | Расстояние от корня до вершины. Уровень корня равен 0. | | ||
| Высота дерева | Длина самого длинного пути от корня до листа. | | ||
| Степень вершины | Количество дочерних вершин. | | ||
| Поддерево | Часть дерева, состоящая из вершины и всех ее потомков. | | ||
|
||
Деревья широко используются в области искусственного интеллекта и в сложных алгоритмах, выступая в качестве эффективного | ||
хранилища информации при решении задач. | ||
|
||
## Обход в глубину | ||
|
||
**DFS** (Depth-First Search) - обход в глубину - это алгоритм обхода графа или дерева, который начинается с начальной | ||
вершины и идет вглубь каждой ветви, пока не достигнет конечной вершины. | ||
|
||
Сложность DFS: O(N), N = V + E, где V - количество вершин, E - количество ребер. | ||
|
||
Пример. | ||
|
||
Дерево: | ||
|
||
```text | ||
A : B, C | ||
B : D, E | ||
C : F | ||
``` | ||
|
||
Визуализируем дерево: | ||
|
||
```text | ||
A | ||
/ \ | ||
B C | ||
/ \ \ | ||
D E F | ||
``` | ||
|
||
Обход в глубину начинается с вершины A и проходит по всем вершинам графа: | ||
|
||
```text | ||
A -> B -> D -> E -> C -> F | ||
``` | ||
|
||
## Двоичные деревья | ||
|
||
**Двоичное дерево** - это дерево, в котором каждая вершина имеет не более двух дочерних вершин. | ||
|
||
## Обход двоичных деревьев | ||
|
||
Виды обходов: | ||
|
||
* **Inorder** (центрированный). Сначала посещаются все узлы в левом поддереве, затем корневой узел, после чего — все | ||
узлы в правом поддереве. | ||
* **Preorder** (прямой). Сначала посещается корневой узел, затем все узлы в левом поддереве и после этого — все узлы в | ||
правом поддереве. | ||
* **Postorder** (обратный). Сначала посещаются все узлы в левом поддереве, затем корневой узел и после этого — все узлы | ||
в правом поддереве. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Задачи по деревьям | ||
|
||
## A. Обход двоичного дерева | ||
|
||
| Поле | Значение | | ||
|-----------|---------------------------------------------------| | ||
| Сложность | Средняя | | ||
| Источник | https://stepik.org/lesson/45970/step/1?unit=24123 | | ||
|
||
Дано двоичное дерево. Обойти дерево с помощью DFS Inorder, DFS Preorder и DFS Postorder. | ||
|
||
Формат входа. Первая строка содержит натуральное число n. Следующие n строк содержат информацию о вершинах дерева. | ||
Каждая строка содержит три числа: номер вершины, номер левой дочерней вершины и номер правой дочерней вершины. | ||
Если у вершины нет дочерних вершин, то соответствующее значение равно -1. | ||
|
||
Формат выхода. Вывести обходы дерева в порядке DFS Inorder, DFS Preorder и DFS Postorder. | ||
|
||
Пример. | ||
|
||
Ввод: | ||
|
||
``` | ||
10 | ||
0 7 2 | ||
10 -1 -1 | ||
20 -1 6 | ||
30 8 9 | ||
40 3 -1 | ||
50 -1 -1 | ||
60 1 -1 | ||
70 5 4 | ||
80 -1 -1 | ||
90 -1 -1 | ||
``` | ||
|
||
Вывод: | ||
|
||
``` | ||
50 70 80 30 90 40 0 20 10 60 | ||
0 70 50 40 30 80 90 20 60 10 | ||
50 80 90 30 40 70 10 60 20 0 | ||
``` | ||
|
||
## B. Высота дерева | ||
|
||
| Поле | Значение | | ||
|-----------|---------------------------------------------------| | ||
| Сложность | Средняя | | ||
| Источник | https://stepik.org/lesson/41234/step/2?unit=19818 | | ||
|
||
Дано дерево. Вершины дерева пронумерованы от 0. Требуется определить высоту дерева. Высотой | ||
вершины называется расстояние от неё до самой удалённой вершины. Высотой дерева называется высота корня дерева. | ||
|
||
Формат входа. Первая строка содержит натуральное число n. Вторая строка содержит n целых чисел | ||
parent[0], ... , parent[n − 1]. Для каждого 0 ≤ i ≤ n−1, parent[i] — родитель вершины i; если parent[i] = −1, | ||
то i является корнем. Гарантируется, что корень ровно один. Гарантируется, что данная последовательность задаёт | ||
дерево. | ||
|
||
Формат выхода. Высота дерева. | ||
|
||
Пример. | ||
|
||
Вход: | ||
|
||
``` | ||
10 | ||
9 7 5 5 2 9 9 9 2 -1 | ||
``` | ||
|
||
Выход: | ||
|
||
``` | ||
4 | ||
``` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# O(n^2) | ||
def tree_height_naive(a: list[int], parent: int = -1) -> int: | ||
height = 0 | ||
# Находим всех детей вершины parent. | ||
children = [i for i, j in enumerate(a) if j == parent] | ||
for child in children: | ||
height = max(height, 1 + tree_height_naive(a, child)) | ||
return height | ||
|
||
|
||
# O(n) | ||
def tree_height_stack(a: list[int]) -> int: | ||
root, adjacency_list = build_tree(a) | ||
return dfs_iterative(root, adjacency_list) | ||
|
||
|
||
# O(n) | ||
def build_tree(a: list[int]) -> tuple[int, list[list[int]]]: | ||
n = len(a) | ||
root = -1 | ||
adjacency_list: list[list[int]] = [[] for _ in range(n)] | ||
for i in range(n): | ||
if a[i] == -1: | ||
root = i | ||
else: | ||
adjacency_list[a[i]].append(i) | ||
return root, adjacency_list | ||
|
||
|
||
# O(n) | ||
def dfs_iterative(root: int, adjacency_list: list[list[int]]) -> int: | ||
n = len(adjacency_list) | ||
# height[i] - высота дерева заканчивающегося в i-й вершине | ||
dp = [1] * n | ||
stack = [root] | ||
visited = [False] * n | ||
while stack: | ||
node = stack.pop() | ||
if not visited[node]: | ||
visited[node] = True | ||
for child in adjacency_list[node]: | ||
stack.append(child) | ||
dp[child] += dp[node] | ||
return max(dp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Задачи по графам | ||
|
||
## A. Камень жадности | ||
|
||
| Поле | Значение | | ||
|-----------|-----------------------------------------| | ||
| Сложность | Сложная | | ||
| Источник | https://stepik.org/lesson/287380/step/1 | | ||
|
||
В одной из мультивселенных могущественный титан Фанос собрал все камни конечности, победил храбрую команду | ||
Блюстителей и поверг мир во тьму. Путешествуя по благодарному миру, Фанос забрел в портовый город у границы | ||
с одной из людских колоний, называемой Р. Местные жители поведали герою легенду о самом большом богатстве | ||
во вселенной – седьмом камне конечности, называемом камень жадности. По слухам, камень открывался только | ||
тому, кто посетит все поселения Р, побывав в каждом ровно один раз (кроме того, в котором начинается | ||
путешествие - в него нужно вернуться) и заплатив при этом самую низкую цену (да, путешествия по Р тоже | ||
имеют свою цену). | ||
|
||
Фанос узнал, что на территории Р работает только один из камней конечности - камень времени. И работает | ||
он специфическим образом – переносит своего владельца в последний момент времени, когда обладатель находился | ||
вне территории Р. Более того, энергия камня жадности настолько сильна, что как только Фанос попадает в поселения | ||
Р, **каждое свое перемещение между поселениями он делает максимально дешевым из возможных**. Однако, находясь | ||
вне Р, Фанос все еще может использовать камень пространства, чтобы мгновенно очутиться в любом из поселений. | ||
|
||
После столь длинной истории наша задача проста – вычислить самый дешевый путь между поселениями Р, | ||
который удалось найти Фаносу. | ||
|
||
Формат входа. Входные данные содержат общее количество поселений n и стоимость путешествия между любой | ||
парой поселений в виде матрицы. Поселения нумеруются от 0 до n-1. | ||
|
||
Формат выхода. В качестве результата необходимо вывести самый короткий путь между поселениями, который удалось | ||
найти Фаносу, а также его стоимость. Если пути не существует, вывести "Lost". | ||
|
||
Пример. | ||
|
||
Ввод: | ||
|
||
``` | ||
4 | ||
0 1 2 3 | ||
4 0 5 6 | ||
7 8 0 9 | ||
10 11 12 0 | ||
``` | ||
|
||
Вывод: | ||
|
||
``` | ||
25 | ||
0 1 2 3 0 | ||
``` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
INF = 10**20 | ||
|
||
|
||
# O(n^3) | ||
def greed_stone(matrix: list[list[int]]) -> tuple[int, list[int]] | None: | ||
n = len(matrix) | ||
# Преобразуем матрицу стоимости так, чтобы стоимость перемещения из города в самого себя была равна | ||
# бесконечности. Это помогает нам убедиться, что мы не останемся в том же городе при выборе следующего | ||
# города для посещения. | ||
matrix = [[(x if x != 0 else INF) for x in costs] for costs in matrix] | ||
# Инициализируем минимальную стоимость и путь. | ||
min_cost, min_path = INF, [] | ||
# Для каждого города мы пытаемся найти путь, начинающийся в этом городе, который посещает все города | ||
# и возвращает нас обратно в начальный город. | ||
for start_city in range(n): | ||
# Инициализируем список посещенных городов и путь. | ||
visited = [False] * n | ||
path, cost = [start_city], 0 | ||
# Инициализируем текущий город. | ||
city = start_city | ||
for _ in range(n - 1): | ||
visited[city] = True | ||
# Выбираем следующий город, который еще не был посещен и имеет минимальную стоимость | ||
# перемещения из текущего города. | ||
next_city, score = min( | ||
((c, matrix[city][c]) for c in range(n) if not visited[c]), | ||
key=lambda x: x[1], | ||
) | ||
# Добавляем выбранный город в путь и увеличиваем общую стоимость на стоимость перемещения до | ||
# выбранного города. | ||
path.append(next_city) | ||
cost += score | ||
# Обновляем текущий город. | ||
city = next_city | ||
# После посещения всех городов мы возвращаемся в начальный город. | ||
# Увеличиваем общую стоимость на стоимость перемещения обратно в начальный город. | ||
cost += matrix[city][start_city] | ||
path.append(start_city) | ||
# Если общая стоимость пути меньше минимальной стоимости пути, который мы нашли до этого, | ||
# обновляем минимальную стоимость и путь. | ||
if cost < min_cost: | ||
min_cost = cost | ||
min_path = path | ||
if min_cost == INF: | ||
return None | ||
return min_cost, min_path |
Oops, something went wrong.