HTML Diff
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>