mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-16 21:50:59 +00:00
fix Ru translation (#1894)
* docs(ru): replace prose quotes with guillemets * docs(ru): replace prose semicolons with periods * docs(ru): align animation title forms * docs(ru): align figure and table references
This commit is contained in:
@@ -30,11 +30,11 @@ $$
|
||||
|
||||

|
||||
|
||||
Мы также можем добавить к ребрам переменную "вес" и получить показанный ниже <u>взвешенный граф (weighted graph)</u>. Например, в мобильных играх вроде Honor of Kings система рассчитывает "близость" между игроками по времени совместной игры, и такую сеть близости можно представить взвешенным графом.
|
||||
Мы также можем добавить к ребрам переменную «вес» и получить <u>взвешенный граф (weighted graph)</u>, как показано на рисунке ниже. Например, в мобильных играх вроде Honor of Kings система рассчитывает «близость» между игроками по времени совместной игры, и такую сеть близости можно представить взвешенным графом.
|
||||
|
||||

|
||||
|
||||
Со структурой данных "граф" связаны следующие основные термины.
|
||||
Со структурой данных «граф» связаны следующие основные термины.
|
||||
|
||||
- <u>Смежность (adjacency)</u>: если между двумя вершинами существует ребро, то такие вершины называются смежными. На рисунке выше с вершиной 1 смежны вершины 2, 3 и 5.
|
||||
- <u>Путь (path)</u>: последовательность ребер от вершины A до вершины B называется путем из A в B. На рисунке выше последовательность ребер 1-5-2-4 является одним из путей от вершины 1 к вершине 4.
|
||||
@@ -42,13 +42,13 @@ $$
|
||||
|
||||
## Представление графа
|
||||
|
||||
Распространенные способы представления графа включают "матрицу смежности" и "список смежности". Ниже для примера рассматривается неориентированный граф.
|
||||
Распространенные способы представления графа включают «матрицу смежности» и «список смежности». Ниже для примера рассматривается неориентированный граф.
|
||||
|
||||
### Матрица смежности
|
||||
|
||||
Пусть число вершин графа равно $n$ ; тогда <u>матрица смежности (adjacency matrix)</u> использует матрицу размера $n \times n$ для представления графа, где каждая строка и каждый столбец соответствуют вершине, а элементы матрицы показывают наличие или отсутствие ребра.
|
||||
Пусть число вершин графа равно $n$. Тогда <u>матрица смежности (adjacency matrix)</u> использует матрицу размера $n \times n$ для представления графа, где каждая строка и каждый столбец соответствуют вершине, а элементы матрицы показывают наличие или отсутствие ребра.
|
||||
|
||||
Как показано на рисунке ниже, обозначим матрицу смежности через $M$ , а список вершин через $V$ ; тогда элемент матрицы $M[i, j] = 1$ означает наличие ребра между вершинами $V[i]$ и $V[j]$ , а элемент $M[i, j] = 0$ означает отсутствие ребра.
|
||||
Как показано на рисунке ниже, обозначим матрицу смежности через $M$ , а список вершин через $V$. Тогда элемент матрицы $M[i, j] = 1$ означает наличие ребра между вершинами $V[i]$ и $V[j]$ , а элемент $M[i, j] = 0$ означает отсутствие ребра.
|
||||
|
||||

|
||||
|
||||
@@ -68,7 +68,7 @@ $$
|
||||
|
||||
Список смежности хранит только реально существующие ребра, а общее число ребер обычно значительно меньше $n^2$ , поэтому он лучше экономит память. Однако для поиска ребра в списке смежности требуется обходить список, поэтому по времени он уступает матрице смежности.
|
||||
|
||||
Если посмотреть на рисунок выше, можно заметить, что **структура списка смежности очень похожа на цепную адресацию в хеш-таблицах, поэтому здесь можно использовать похожие методы оптимизации эффективности**. Например, если список слишком длинный, его можно преобразовать в AVL-дерево или красно-черное дерево, чтобы снизить временную сложность с $O(n)$ до $O(\log n)$ ; также список можно преобразовать в хеш-таблицу, чтобы довести временную сложность до $O(1)$ .
|
||||
Как видно на рисунке выше, **структура списка смежности очень похожа на цепную адресацию в хеш-таблицах, поэтому здесь можно использовать похожие методы оптимизации эффективности**. Например, если список слишком длинный, его можно преобразовать в AVL-дерево или красно-черное дерево, чтобы снизить временную сложность с $O(n)$ до $O(\log n)$. Также список можно преобразовать в хеш-таблицу, чтобы довести временную сложность до $O(1)$ .
|
||||
|
||||
## Типичные применения графов
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Базовые операции графа
|
||||
|
||||
Базовые операции графа можно разделить на операции над "ребрами" и операции над "вершинами". При двух способах представления, "матрице смежности" и "списке смежности", реализация этих операций различается.
|
||||
Базовые операции графа можно разделить на операции над «ребрами» и операции над «вершинами». При двух способах представления, «матрице смежности» и «списке смежности», реализация этих операций различается.
|
||||
|
||||
## Реализация на основе матрицы смежности
|
||||
|
||||
Пусть дан неориентированный граф с числом вершин $n$ . Тогда способы реализации различных операций показаны на рисунках ниже.
|
||||
Пусть дан неориентированный граф с числом вершин $n$ . Тогда способы реализации различных операций показаны на рисунке ниже.
|
||||
|
||||
- **Добавление или удаление ребра**: достаточно изменить соответствующее ребро в матрице смежности, что требует $O(1)$ времени. Поскольку граф неориентированный, необходимо одновременно обновить ребра в обоих направлениях.
|
||||
- **Добавление вершины**: в конец матрицы смежности добавляется строка и столбец, полностью заполненные нулями; это требует $O(n)$ времени.
|
||||
- **Удаление вершины**: из матрицы смежности удаляется строка и столбец. При удалении первой строки и первого столбца достигается худший случай, когда требуется "сдвинуть влево вверх" $(n-1)^2$ элементов, поэтому используется $O(n^2)$ времени.
|
||||
- **Инициализация**: передаются $n$ вершин, затем инициализируется список вершин `vertices` длины $n$ , что требует $O(n)$ времени; после этого инициализируется матрица смежности `adjMat` размера $n \times n$ , что требует $O(n^2)$ времени.
|
||||
- **Добавление вершины**: в конец матрицы смежности добавляется строка и столбец, полностью заполненные нулями. Это требует $O(n)$ времени.
|
||||
- **Удаление вершины**: из матрицы смежности удаляется строка и столбец. При удалении первой строки и первого столбца достигается худший случай, когда требуется «сдвинуть влево вверх» $(n-1)^2$ элементов, поэтому используется $O(n^2)$ времени.
|
||||
- **Инициализация**: передаются $n$ вершин, затем инициализируется список вершин `vertices` длины $n$ , что требует $O(n)$ времени. После этого инициализируется матрица смежности `adjMat` размера $n \times n$ , что требует $O(n^2)$ времени.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -34,13 +34,13 @@
|
||||
|
||||
## Реализация на основе списка смежности
|
||||
|
||||
Пусть неориентированный граф содержит в сумме $n$ вершин и $m$ ребер. Тогда различные операции можно реализовать способом, показанным на рисунках ниже.
|
||||
Пусть неориентированный граф содержит в сумме $n$ вершин и $m$ ребер. Тогда различные операции можно реализовать способом, показанным на рисунке ниже.
|
||||
|
||||
- **Добавление ребра**: достаточно добавить ребро в конец списка, соответствующего вершине; это требует $O(1)$ времени. Поскольку граф неориентированный, необходимо одновременно добавить ребра в обоих направлениях.
|
||||
- **Удаление ребра**: нужно найти и удалить указанное ребро в списке, соответствующем вершине; это требует $O(m)$ времени. В неориентированном графе необходимо удалить ребра в обоих направлениях.
|
||||
- **Добавление вершины**: в список смежности добавляется еще один список, а новая вершина становится его головным узлом; это требует $O(1)$ времени.
|
||||
- **Удаление вершины**: требуется пройти по всему списку смежности и удалить все ребра, содержащие указанную вершину; это требует $O(n + m)$ времени.
|
||||
- **Инициализация**: в списке смежности создаются $n$ вершин и $2m$ ребер; это требует $O(n + m)$ времени.
|
||||
- **Добавление ребра**: достаточно добавить ребро в конец списка, соответствующего вершине. Это требует $O(1)$ времени. Поскольку граф неориентированный, необходимо одновременно добавить ребра в обоих направлениях.
|
||||
- **Удаление ребра**: нужно найти и удалить указанное ребро в списке, соответствующем вершине. Это требует $O(m)$ времени. В неориентированном графе необходимо удалить ребра в обоих направлениях.
|
||||
- **Добавление вершины**: в список смежности добавляется еще один список, а новая вершина становится его головным узлом. Это требует $O(1)$ времени.
|
||||
- **Удаление вершины**: требуется пройти по всему списку смежности и удалить все ребра, содержащие указанную вершину. Это требует $O(n + m)$ времени.
|
||||
- **Инициализация**: в списке смежности создаются $n$ вершин и $2m$ ребер. Это требует $O(n + m)$ времени.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -57,7 +57,7 @@
|
||||
=== "<5>"
|
||||

|
||||
|
||||
Ниже приведен код списка смежности. По сравнению с рисунками выше, реальная реализация имеет следующие отличия.
|
||||
Ниже приведен код списка смежности. По сравнению с тем, что показано на рисунке выше, реальная реализация имеет следующие отличия.
|
||||
|
||||
- Чтобы упростить добавление и удаление вершин, а также сделать код проще, мы используем список, то есть динамический массив, вместо связного списка.
|
||||
- Для хранения списка смежности используется хеш-таблица, где `key` - это экземпляр вершины, а `value` - список смежных вершин данной вершины.
|
||||
@@ -83,4 +83,4 @@
|
||||
| Удаление вершины | $O(n^2)$ | $O(n + m)$ | $O(n)$ |
|
||||
| Занимаемая память | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ |
|
||||
|
||||
Если смотреть только на таблицу, может показаться, что список смежности на основе хеш-таблицы является лучшим и по времени, и по памяти. Но на практике операции над ребрами в матрице смежности обычно выполняются быстрее, потому что там нужен лишь один доступ к массиву или одно присваивание. В целом матрица смежности воплощает принцип "обмена пространства на время", а список смежности - принцип "обмена времени на пространство".
|
||||
Если судить только по данным в таблице выше, может показаться, что список смежности на основе хеш-таблицы является лучшим и по времени, и по памяти. Но на практике операции над ребрами в матрице смежности обычно выполняются быстрее, потому что там нужен лишь один доступ к массиву или одно присваивание. В целом матрица смежности воплощает принцип «обмена пространства на время», а список смежности - принцип «обмена времени на пространство».
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Обход графа
|
||||
|
||||
Дерево представляет отношение "один ко многим", тогда как граф обладает большей свободой и может выражать произвольные отношения "многие ко многим". Поэтому дерево можно рассматривать как частный случай графа. Очевидно, что **операции обхода дерева также являются частным случаем операций обхода графа**.
|
||||
Дерево представляет отношение «один ко многим», тогда как граф обладает большей свободой и может выражать произвольные отношения «многие ко многим». Поэтому дерево можно рассматривать как частный случай графа. Очевидно, что **операции обхода дерева также являются частным случаем операций обхода графа**.
|
||||
|
||||
И графы, и деревья требуют применения алгоритмов обхода. Способы обхода графа также делятся на два типа: <u>обход в ширину</u> и <u>обход в глубину</u>.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
### Реализация алгоритма
|
||||
|
||||
BFS обычно реализуется с помощью очереди, код приведен ниже. Очередь обладает свойством "первым пришел - первым вышел", что хорошо соответствует идее BFS "от ближнего к дальнему".
|
||||
BFS обычно реализуется с помощью очереди, код приведен ниже. Очередь обладает свойством «первым пришел - первым вышел», что хорошо соответствует идее BFS «от ближнего к дальнему».
|
||||
|
||||
1. Поместить стартовую вершину обхода `startVet` в очередь и запустить цикл.
|
||||
2. На каждой итерации цикла извлекать вершину из головы очереди и записывать ее посещение, после чего добавлять все смежные вершины этой вершины в хвост очереди.
|
||||
@@ -28,7 +28,7 @@ BFS обычно реализуется с помощью очереди, код
|
||||
[file]{graph_bfs}-[class]{}-[func]{graph_bfs}
|
||||
```
|
||||
|
||||
Код сравнительно абстрактен, поэтому рекомендуется сверяться с рисунками ниже для лучшего понимания.
|
||||
Код сравнительно абстрактен, поэтому для лучшего понимания рекомендуется сопоставлять его с тем, что показано на рисунке ниже.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -65,11 +65,11 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
!!! question "Является ли последовательность обхода в ширину единственной?"
|
||||
|
||||
Нет. Обход в ширину требует только соблюдения порядка "от ближнего к дальнему", **а порядок обхода нескольких вершин на одинаковом расстоянии может произвольно меняться**. Например, на рисунке выше можно поменять местами порядок посещения вершин $1$ и $3$ , а вершины $2$, $4$, $6$ также можно переставлять произвольно.
|
||||
Нет. Обход в ширину требует только соблюдения порядка «от ближнего к дальнему», **а порядок обхода нескольких вершин на одинаковом расстоянии может произвольно меняться**. Например, на рисунке выше можно поменять местами порядок посещения вершин $1$ и $3$ , а вершины $2$, $4$, $6$ также можно переставлять произвольно.
|
||||
|
||||
### Анализ сложности
|
||||
|
||||
**Временная сложность**: все вершины по одному разу помещаются в очередь и извлекаются из нее, что требует $O(|V|)$ времени; при обходе смежных вершин, поскольку граф неориентированный, все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; в сумме получается $O(|V| + |E|)$ .
|
||||
**Временная сложность**: все вершины по одному разу помещаются в очередь и извлекаются из нее, что требует $O(|V|)$ времени. При обходе смежных вершин, поскольку граф неориентированный, все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени. В сумме получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: список `res` , хеш-множество `visited` и очередь `que` в худшем случае могут содержать до $|V|$ вершин, поэтому требуется $O(|V|)$ памяти.
|
||||
|
||||
@@ -81,18 +81,18 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
### Реализация алгоритма
|
||||
|
||||
Такой алгоритмический шаблон "дойти до конца и вернуться" обычно реализуется через рекурсию. Подобно обходу в ширину, в обходе в глубину мы также используем хеш-множество `visited` для записи уже посещенных вершин и тем самым избегаем повторного посещения.
|
||||
Такой алгоритмический шаблон «дойти до конца и вернуться» обычно реализуется через рекурсию. Подобно обходу в ширину, в обходе в глубину мы также используем хеш-множество `visited` для записи уже посещенных вершин и тем самым избегаем повторного посещения.
|
||||
|
||||
```src
|
||||
[file]{graph_dfs}-[class]{}-[func]{graph_dfs}
|
||||
```
|
||||
|
||||
Алгоритмический процесс обхода в глубину показан на рисунках ниже.
|
||||
Алгоритмический процесс обхода в глубину показан на рисунке ниже.
|
||||
|
||||
- **Прямая пунктирная линия обозначает нисходящую рекурсию** , то есть запуск нового рекурсивного метода для посещения новой вершины.
|
||||
- **Изогнутая пунктирная линия обозначает восходящую рекурсию** , то есть данный рекурсивный метод завершился и управление вернулось туда, откуда он был вызван.
|
||||
|
||||
Чтобы лучше понять алгоритм, рекомендуется совместить рисунки ниже с кодом и мысленно проследить весь процесс DFS, включая моменты запуска и возврата каждого рекурсивного вызова.
|
||||
Чтобы лучше понять алгоритм, рекомендуется сопоставить код с тем, что показано на рисунке ниже, и мысленно проследить весь процесс DFS, включая моменты запуска и возврата каждого рекурсивного вызова.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -130,11 +130,11 @@ BFS обычно реализуется с помощью очереди, код
|
||||
!!! question "Является ли последовательность обхода в глубину единственной?"
|
||||
|
||||
Как и в случае обхода в ширину, последовательность DFS тоже не является единственной. Для заданной вершины допустимо сначала углубиться в любое направление, то есть порядок смежных вершин может быть произвольным, и все такие варианты будут корректными обходами в глубину.
|
||||
|
||||
Если взять в качестве примера обход дерева, то варианты "корень $\rightarrow$ лево $\rightarrow$ право", "лево $\rightarrow$ корень $\rightarrow$ право" и "лево $\rightarrow$ право $\rightarrow$ корень" соответствуют прямому, симметричному и обратному обходам соответственно. Они показывают три разных приоритета обхода, но все они относятся к обходу в глубину.
|
||||
|
||||
Если взять в качестве примера обход дерева, то варианты «корень $\rightarrow$ лево $\rightarrow$ право», «лево $\rightarrow$ корень $\rightarrow$ право» и «лево $\rightarrow$ право $\rightarrow$ корень» соответствуют прямому, симметричному и обратному обходам соответственно. Они показывают три разных приоритета обхода, но все они относятся к обходу в глубину.
|
||||
|
||||
### Анализ сложности
|
||||
|
||||
**Временная сложность**: все вершины будут посещены по $1$ разу, что требует $O(|V|)$ времени; все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; суммарно получается $O(|V| + |E|)$ .
|
||||
**Временная сложность**: все вершины будут посещены по $1$ разу, что требует $O(|V|)$ времени. Все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени. Суммарно получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: число вершин в списке `res` и хеш-множестве `visited` в худшем случае достигает $|V|$ , максимальная глубина рекурсии тоже равна $|V|$ , поэтому требуется $O(|V|)$ памяти.
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
!!! abstract
|
||||
|
||||
В жизни мы похожи на вершины, соединенные множеством невидимых ребер.
|
||||
|
||||
|
||||
Каждая встреча и каждое расставание оставляют в этой огромной сети свой след.
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
- По сравнению с линейными отношениями (связный список) и отношениями разделения (дерево), сетевые отношения (граф) обладают большей свободой и потому более сложны.
|
||||
- Ребра ориентированного графа имеют направление, в связном графе любые вершины достижимы, а во взвешенном графе каждое ребро содержит переменную веса.
|
||||
- Матрица смежности использует матрицу для представления графа: каждая строка и каждый столбец соответствуют вершине, а элементы матрицы показывают, есть между двумя вершинами ребро или нет. Матрица смежности эффективна в операциях добавления, удаления, поиска и изменения, но расходует больше памяти.
|
||||
- Список смежности использует несколько списков для представления графа; $i$-й список соответствует вершине $i$ и хранит все ее смежные вершины. По сравнению с матрицей смежности список смежности экономит пространство, но для поиска ребра в нем приходится обходить список, поэтому по времени он уступает.
|
||||
- Список смежности использует несколько списков для представления графа. $i$-й список соответствует вершине $i$ и хранит все ее смежные вершины. По сравнению с матрицей смежности список смежности экономит пространство, но для поиска ребра в нем приходится обходить список, поэтому по времени он уступает.
|
||||
- Когда списки в списке смежности становятся слишком длинными, их можно преобразовать в красно-черное дерево или хеш-таблицу, чтобы повысить эффективность поиска.
|
||||
- С точки зрения алгоритмической идеи матрица смежности отражает принцип "обмена пространства на время", а список смежности - принцип "обмена времени на пространство".
|
||||
- С точки зрения алгоритмической идеи матрица смежности отражает принцип «обмена пространства на время», а список смежности - принцип «обмена времени на пространство».
|
||||
- Графы можно использовать для моделирования различных реальных систем, таких как социальные сети, линии метро и так далее.
|
||||
- Дерево является частным случаем графа, а обход дерева - частным случаем обхода графа.
|
||||
- Обход графа в ширину представляет собой способ поиска, который расширяется от ближнего к дальнему и обычно реализуется с помощью очереди.
|
||||
- Обход графа в глубину представляет собой способ поиска, который сначала идет до самого конца, а затем возвращается назад, когда путь исчерпан; обычно он реализуется на основе рекурсии.
|
||||
- Обход графа в глубину представляет собой способ поиска, который сначала идет до самого конца, а затем возвращается назад, когда путь исчерпан. Обычно он реализуется на основе рекурсии.
|
||||
|
||||
### Q & A
|
||||
|
||||
**Q**: Что считается путем: последовательность вершин или последовательность ребер?
|
||||
|
||||
Определение в разных языковых версиях Википедии различается: в английской версии путь определяется как "последовательность ребер", а в русской версии - как "последовательность вершин". В английской версии исходная формулировка выглядит так: In graph theory, a path in a graph is a finite or infinite sequence of edges which joins a sequence of vertices.
|
||||
Определение в разных языковых версиях Википедии различается: в английской версии путь определяется как «последовательность ребер», а в русской версии - как «последовательность вершин». В английской версии исходная формулировка выглядит так: In graph theory, a path in a graph is a finite or infinite sequence of edges which joins a sequence of vertices.
|
||||
|
||||
В этой книге путь рассматривается как последовательность ребер, а не как последовательность вершин. Причина в том, что между двумя вершинами может существовать несколько ребер, и в таком случае каждому ребру соответствует свой путь.
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
|
||||
В несвязном графе, начиная из некоторой вершины, по крайней мере одна вершина оказывается недостижимой. Чтобы обойти весь несвязный граф, нужно задать несколько стартовых точек и обойти все связные компоненты графа.
|
||||
|
||||
**Q**: Есть ли требования к порядку вершин в списке "всех вершин, соединенных с данной вершиной" в списке смежности?
|
||||
**Q**: Есть ли требования к порядку вершин в списке «всех вершин, соединенных с данной вершиной» в списке смежности?
|
||||
|
||||
Порядок может быть произвольным. Но на практике может понадобиться сортировка по определенному правилу, например по порядку добавления вершин или по возрастанию значений вершин; это помогает быстро находить вершины с некоторым экстремальным свойством.
|
||||
Порядок может быть произвольным. Но на практике может понадобиться сортировка по определенному правилу, например по порядку добавления вершин или по возрастанию значений вершин. Это помогает быстро находить вершины с некоторым экстремальным свойством.
|
||||
|
||||
Reference in New Issue
Block a user