0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>В этой статье я хотел бы представить свой топ-10 алгоритмов, которые могут помочь вам в работе. Бонусом покажу на примерах, как ими пользоваться.</strong></p>
1
<p><strong>В этой статье я хотел бы представить свой топ-10 алгоритмов, которые могут помочь вам в работе. Бонусом покажу на примерах, как ими пользоваться.</strong></p>
2
<p>Сам список включает в себя следующие алгоритмы:</p>
2
<p>Сам список включает в себя следующие алгоритмы:</p>
3
<ol><li><p>QuickSort - алгоритм разделения и покорения, который рекурсивно разбивает массив на более мелкие подмассивы и сортирует их.</p>
3
<ol><li><p>QuickSort - алгоритм разделения и покорения, который рекурсивно разбивает массив на более мелкие подмассивы и сортирует их.</p>
4
</li>
4
</li>
5
<li><p>MergeSort - алгоритм "разделяй и властвуй", который рекурсивно делит массив пополам, сортирует половинки, а затем объединяет их обратно.</p>
5
<li><p>MergeSort - алгоритм "разделяй и властвуй", который рекурсивно делит массив пополам, сортирует половинки, а затем объединяет их обратно.</p>
6
</li>
6
</li>
7
<li><p>Бинарный поиск - алгоритм поиска, который многократно делит интервал поиска пополам, ища определенное значение в отсортированном массиве.</p>
7
<li><p>Бинарный поиск - алгоритм поиска, который многократно делит интервал поиска пополам, ища определенное значение в отсортированном массиве.</p>
8
</li>
8
</li>
9
<li><p>Поиск в глубину (DFS) - алгоритм обхода графа, который исследует как можно дальше по каждой ветви, прежде чем вернуться назад.</p>
9
<li><p>Поиск в глубину (DFS) - алгоритм обхода графа, который исследует как можно дальше по каждой ветви, прежде чем вернуться назад.</p>
10
</li>
10
</li>
11
<li><p>Breadth-first Search (BFS) - алгоритм обхода графа, который исследует все вершины графа или все узлы дерева уровень за уровнем.</p>
11
<li><p>Breadth-first Search (BFS) - алгоритм обхода графа, который исследует все вершины графа или все узлы дерева уровень за уровнем.</p>
12
</li>
12
</li>
13
<li><p>Динамическое программирование - метод решения проблем путем разбиения их на более мелкие подпроблемы и хранения решений этих подпроблем, чтобы избежать лишней работы.</p>
13
<li><p>Динамическое программирование - метод решения проблем путем разбиения их на более мелкие подпроблемы и хранения решений этих подпроблем, чтобы избежать лишней работы.</p>
14
</li>
14
</li>
15
<li><p>Алгоритм Беллмана-Форда - алгоритм кратчайшего пути с одним источником, который также может обнаруживать отрицательные циклы в графе.</p>
15
<li><p>Алгоритм Беллмана-Форда - алгоритм кратчайшего пути с одним источником, который также может обнаруживать отрицательные циклы в графе.</p>
16
</li>
16
</li>
17
<li><p>Алгоритм Дейкстры - алгоритм кратчайшего пути с одним источником, который работает путем многократного ослабления оценок расстояния между вершинами.</p>
17
<li><p>Алгоритм Дейкстры - алгоритм кратчайшего пути с одним источником, который работает путем многократного ослабления оценок расстояния между вершинами.</p>
18
</li>
18
</li>
19
<li><p>Алгоритм поиска A* - алгоритм поиска, который использует эвристику для направления поиска и обычно используется для поиска пути и решения задач обхода графа.</p>
19
<li><p>Алгоритм поиска A* - алгоритм поиска, который использует эвристику для направления поиска и обычно используется для поиска пути и решения задач обхода графа.</p>
20
</li>
20
</li>
21
<li><p>Knapsack Problem - задача комбинаторной оптимизации, целью которой является максимизация общей стоимости предметов в рюкзаке без превышения его вместимости.</p>
21
<li><p>Knapsack Problem - задача комбинаторной оптимизации, целью которой является максимизация общей стоимости предметов в рюкзаке без превышения его вместимости.</p>
22
</li>
22
</li>
23
</ol><p>Для примерах реализации я буду использовать Typescript, так как это ЯП с сильной типизацией, но в тоже время достаточно простой для понимания.</p>
23
</ol><p>Для примерах реализации я буду использовать Typescript, так как это ЯП с сильной типизацией, но в тоже время достаточно простой для понимания.</p>
24
<blockquote><p>Заметка: все вышеперечисленные алгоритмы реализованы с помощью Typescript и могут быть найдены в менеджере пакетов npm.</p>
24
<blockquote><p>Заметка: все вышеперечисленные алгоритмы реализованы с помощью Typescript и могут быть найдены в менеджере пакетов npm.</p>
25
</blockquote><p>Реализацию<strong>QuickSort</strong>,<strong>MergeSort</strong>,<strong>Бинарного поиска</strong>,<strong>Поиска в глубину (DFS)</strong>мы можете найти в<a>первой части</a>.</p>
25
</blockquote><p>Реализацию<strong>QuickSort</strong>,<strong>MergeSort</strong>,<strong>Бинарного поиска</strong>,<strong>Поиска в глубину (DFS)</strong>мы можете найти в<a>первой части</a>.</p>
26
<p>Сегодня мы разберем<strong>Breadth-first Search (BFS)</strong>,<strong>Динамическое программирование</strong>и<strong>Алгоритм Беллмана-Форда</strong></p>
26
<p>Сегодня мы разберем<strong>Breadth-first Search (BFS)</strong>,<strong>Динамическое программирование</strong>и<strong>Алгоритм Беллмана-Форда</strong></p>
27
<h3>Поехали! 🚀</h3>
27
<h3>Поехали! 🚀</h3>
28
<p><strong>Пример алгоритма поиска в ширину (BFS), реализованного на TypeScript:</strong></p>
28
<p><strong>Пример алгоритма поиска в ширину (BFS), реализованного на TypeScript:</strong></p>
29
<p>Данная реализация BFS использует представление графа в виде списка смежности, где каждая вершина представлена ключом в карте, а ее значение - массив соседних вершин.</p>
29
<p>Данная реализация BFS использует представление графа в виде списка смежности, где каждая вершина представлена ключом в карте, а ее значение - массив соседних вершин.</p>
30
<p>Функция<strong>bfs</strong>начинает работу с инициализации очереди, добавления в нее начальной вершины и пометки ее как посещенной. Затем с помощью цикла while выполняется итерация по очереди, удаление первой вершины, ее печать, добавление в очередь всех ее непосещенных соседей и пометка их как посещенных.</p>
30
<p>Функция<strong>bfs</strong>начинает работу с инициализации очереди, добавления в нее начальной вершины и пометки ее как посещенной. Затем с помощью цикла while выполняется итерация по очереди, удаление первой вершины, ее печать, добавление в очередь всех ее непосещенных соседей и пометка их как посещенных.</p>
31
<p>Стоит отметить, что в данном примере показан обход BFS, начиная с вершины 0, но его можно начать с любой вершины, и для реализации BFS полезно использовать структуру данных очереди.</p>
31
<p>Стоит отметить, что в данном примере показан обход BFS, начиная с вершины 0, но его можно начать с любой вершины, и для реализации BFS полезно использовать структуру данных очереди.</p>
32
<p>Кроме того, это базовая реализация BFS, и ее можно использовать для различных целей, таких как поиск кратчайшего пути между двумя вершинами, проверка существования пути между двумя вершинами и решение таких задач, как обход лабиринта и поиск кратчайшего пути в сетке.</p>
32
<p>Кроме того, это базовая реализация BFS, и ее можно использовать для различных целей, таких как поиск кратчайшего пути между двумя вершинами, проверка существования пути между двумя вершинами и решение таких задач, как обход лабиринта и поиск кратчайшего пути в сетке.</p>
33
<p>Детальное объяснение можете найти<a>тут</a></p>
33
<p>Детальное объяснение можете найти<a>тут</a></p>
34
<p><strong>Динамическое программирование</strong></p>
34
<p><strong>Динамическое программирование</strong></p>
35
<p>Динамическое программирование (ДП) - это метод решения проблем путем разбиения их на более мелкие подпроблемы и хранения решений этих подпроблем, чтобы избежать лишней работы. Этот подход особенно полезен для решения, которые имеют перекрывающиеся подпроблемы, т.е. одни и те же подпроблемы решаются несколько раз.</p>
35
<p>Динамическое программирование (ДП) - это метод решения проблем путем разбиения их на более мелкие подпроблемы и хранения решений этих подпроблем, чтобы избежать лишней работы. Этот подход особенно полезен для решения, которые имеют перекрывающиеся подпроблемы, т.е. одни и те же подпроблемы решаются несколько раз.</p>
36
<p>Существует две основные техники, используемые в динамическом программировании:</p>
36
<p>Существует две основные техники, используемые в динамическом программировании:</p>
37
<p>-<em>Мемоизация:</em>Этот метод предполагает хранение решений подпроблем в кэше или таблице, чтобы их можно было использовать в дальнейшем. -<em>Табулирование:</em>Эта техника предполагает заполнение таблицы снизу вверх, начиная с наименьших подпроблем и доводя до окончательного решения.</p>
37
<p>-<em>Мемоизация:</em>Этот метод предполагает хранение решений подпроблем в кэше или таблице, чтобы их можно было использовать в дальнейшем. -<em>Табулирование:</em>Эта техника предполагает заполнение таблицы снизу вверх, начиная с наименьших подпроблем и доводя до окончательного решения.</p>
38
<p>Вот пример использования динамического программирования для решения проблемы последовательности Фибоначчи:</p>
38
<p>Вот пример использования динамического программирования для решения проблемы последовательности Фибоначчи:</p>
39
<p>В этом примере функция fibonacci принимает на вход число n и массив memo и возвращает n-е число последовательности Фибоначчи, используя мемоизацию. Функция проверяет, хранится ли уже результат для текущего n в массиве memo, если да, то возвращает его, в противном случае вычисляет результат путем сложения результатов (n-1)-го и (n-2)-го чисел Фибоначчи и сохраняет его в массиве memo для дальнейшего использования.</p>
39
<p>В этом примере функция fibonacci принимает на вход число n и массив memo и возвращает n-е число последовательности Фибоначчи, используя мемоизацию. Функция проверяет, хранится ли уже результат для текущего n в массиве memo, если да, то возвращает его, в противном случае вычисляет результат путем сложения результатов (n-1)-го и (n-2)-го чисел Фибоначчи и сохраняет его в массиве memo для дальнейшего использования.</p>
40
<p>Этот подход имеет временную сложность O(n), поскольку мы вычисляем каждое число последовательности Фибоначчи только один раз и можем повторно использовать результаты предыдущих вычислений.</p>
40
<p>Этот подход имеет временную сложность O(n), поскольку мы вычисляем каждое число последовательности Фибоначчи только один раз и можем повторно использовать результаты предыдущих вычислений.</p>
41
<p>Динамическое программирование может быть применено ко многим проблемам, таким как проблема кратчайшего пути, проблема ранца и некоторые проблемы оптимизации. Важно понять суть проблемы, прежде чем применять динамическое программирование, так как некоторые проблемы не могут быть решены с помощью этого метода. Также важно понимать временную и пространственную сложность решения.</p>
41
<p>Динамическое программирование может быть применено ко многим проблемам, таким как проблема кратчайшего пути, проблема ранца и некоторые проблемы оптимизации. Важно понять суть проблемы, прежде чем применять динамическое программирование, так как некоторые проблемы не могут быть решены с помощью этого метода. Также важно понимать временную и пространственную сложность решения.</p>
42
<p><strong>Алгоритм Беллмана-Форда</strong></p>
42
<p><strong>Алгоритм Беллмана-Форда</strong></p>
43
<p>Алгоритм Форда-Беллмана позволяет найти кратчайшие пути из одной вершины графа до всех остальных, даже для графов, в которых веса ребер могут быть отрицательными. Тем не менее, в графе не должно быть циклов отрицательного веса, достижимых из начальной вершины, иначе вопрос о кратчайших путях является бессмысленным. При этом алгоритм Форда-Беллмана позволяет определить наличие циклов отрицательного веса, достижимых из начальной вершины.</p>
43
<p>Алгоритм Форда-Беллмана позволяет найти кратчайшие пути из одной вершины графа до всех остальных, даже для графов, в которых веса ребер могут быть отрицательными. Тем не менее, в графе не должно быть циклов отрицательного веса, достижимых из начальной вершины, иначе вопрос о кратчайших путях является бессмысленным. При этом алгоритм Форда-Беллмана позволяет определить наличие циклов отрицательного веса, достижимых из начальной вершины.</p>
44
<p>Алгоритм Форда-Беллмана использует динамическое программирование. Введем функцию динамического программирования:</p>
44
<p>Алгоритм Форда-Беллмана использует динамическое программирование. Введем функцию динамического программирования:</p>
45
<p>- F[k][i] - длина кратчайшего пути из начальной вершины до вершины i, содержащего не более k ребер.</p>
45
<p>- F[k][i] - длина кратчайшего пути из начальной вершины до вершины i, содержащего не более k ребер.</p>
46
<p>Начальные значения зададим для случая k=0. В этом случае F[0][start] = 0, а для всех остальных вершин i F[0][i] = INF, то есть путь, состоящий из нуля ребер существует только от вершины start до вершины start, а до остальных вершин пути из нуля ребер не существует, что будем отмечать значением INF.</p>
46
<p>Начальные значения зададим для случая k=0. В этом случае F[0][start] = 0, а для всех остальных вершин i F[0][i] = INF, то есть путь, состоящий из нуля ребер существует только от вершины start до вершины start, а до остальных вершин пути из нуля ребер не существует, что будем отмечать значением INF.</p>
47
<p>Далее будем вычислять значения функции F увеличивая число ребер в пути k, то есть вычислим кратчайшие пути, содержащие не более 1 ребра, кратчайшие пути, содержащие не более 2 ребер и т. д. Если в графе нет циклов отрицательного веса, то кратчайший путь между любыми двумя вершинами содержит не более ребра ( - число вершин в графе), поэтому нужно вычислить значения F[n-1][i], которые и будут длинами кратчайших путей от вершины start до вершины i).</p>
47
<p>Далее будем вычислять значения функции F увеличивая число ребер в пути k, то есть вычислим кратчайшие пути, содержащие не более 1 ребра, кратчайшие пути, содержащие не более 2 ребер и т. д. Если в графе нет циклов отрицательного веса, то кратчайший путь между любыми двумя вершинами содержит не более ребра ( - число вершин в графе), поэтому нужно вычислить значения F[n-1][i], которые и будут длинами кратчайших путей от вершины start до вершины i).</p>
48
<p>Рассмотрим, как вычисляется значение F[k][i]. Пусть есть кратчайший маршрут из вершины start до вершины i, содержащий не более k ребер. Пусть последнее ребро этого маршрута есть ребро j-i. Тогда путь до вершины j содержит не более k-1 ребра и является кратчайшим путем из всех таких путей, значит, его длина равна F[k-1][j], а длина пути до вершины i равна F[k-1][j] + W[j][i], где W[j][i] есть вес ребра j-i. Дальше необходимо перебрать все вершины j, которые могут выступать в качестве предыдущих, и выбрать минимальное значение F[k-1][j] + W[j][i].</p>
48
<p>Рассмотрим, как вычисляется значение F[k][i]. Пусть есть кратчайший маршрут из вершины start до вершины i, содержащий не более k ребер. Пусть последнее ребро этого маршрута есть ребро j-i. Тогда путь до вершины j содержит не более k-1 ребра и является кратчайшим путем из всех таких путей, значит, его длина равна F[k-1][j], а длина пути до вершины i равна F[k-1][j] + W[j][i], где W[j][i] есть вес ребра j-i. Дальше необходимо перебрать все вершины j, которые могут выступать в качестве предыдущих, и выбрать минимальное значение F[k-1][j] + W[j][i].</p>
49
<p>Получаем следующий алгоритм:</p>
49
<p>Получаем следующий алгоритм:</p>
50
<p>Реализацию на других языках можно найти<a>тут</a>и<a>тут</a></p>
50
<p>Реализацию на других языках можно найти<a>тут</a>и<a>тут</a></p>
51
<h3>На этом все!</h3>
51
<h3>На этом все!</h3>
52
<p>Спасибо за ваше внимание. В следующей части реализуем такие алгоритмы как Knapsack Problem и Алгоритм поиска A*.</p>
52
<p>Спасибо за ваше внимание. В следующей части реализуем такие алгоритмы как Knapsack Problem и Алгоритм поиска A*.</p>