Add ru version (#1865)
* Add Russian docs site baseline * Add Russian localized codebase * Polish Russian code wording * Update ru code translation. * Update code translation and chapter covers. * Fix pythontutor extraction. * Add README and landing page. * placeholder of profiles * Use figures of English version * Remove chapter paperbook
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,83 @@
|
||||
# Граф
|
||||
|
||||
<u>Граф (graph)</u> - это нелинейная структура данных, состоящая из <u>вершин (vertex)</u> и <u>ребер (edge)</u>. Мы можем абстрактно представить граф $G$ как множество вершин $V$ и множество ребер $E$ . В примере ниже показан граф, содержащий 5 вершин и 7 ребер.
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
V & = \{ 1, 2, 3, 4, 5 \} \newline
|
||||
E & = \{ (1,2), (1,3), (1,5), (2,3), (2,4), (2,5), (4,5) \} \newline
|
||||
G & = \{ V, E \} \newline
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
Если рассматривать вершины как узлы, а ребра как ссылки (указатели), соединяющие эти узлы, то граф можно считать структурой данных, выросшей из связного списка. Как показано на рисунке ниже, **по сравнению с линейными отношениями (связный список) и отношениями разделения (дерево), сетевые отношения (граф) обладают большей свободой** , а потому и сложнее.
|
||||
|
||||

|
||||
|
||||
## Распространенные типы и термины графов
|
||||
|
||||
В зависимости от того, имеют ли ребра направление, графы делятся на <u>неориентированные графы (undirected graph)</u> и <u>ориентированные графы (directed graph)</u> , как показано на рисунке ниже.
|
||||
|
||||
- В неориентированном графе ребро означает "двустороннюю" связь между двумя вершинами, например отношение "друзья" в WeChat или QQ.
|
||||
- В ориентированном графе ребро имеет направление, то есть ребра $A \rightarrow B$ и $A \leftarrow B$ независимы друг от друга, как, например, отношения "подписка" и "подписчик" в Weibo или Douyin.
|
||||
|
||||

|
||||
|
||||
В зависимости от того, достижимы ли все вершины друг из друга, граф делится на <u>связный граф (connected graph)</u> и <u>несвязный граф (disconnected graph)</u> , как показано на рисунке ниже.
|
||||
|
||||
- В связном графе, начиная из некоторой вершины, можно добраться до любой другой вершины.
|
||||
- В несвязном графе, начиная из некоторой вершины, по крайней мере одна вершина оказывается недостижимой.
|
||||
|
||||

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

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

|
||||
|
||||
Матрица смежности обладает следующими особенностями.
|
||||
|
||||
- В простом графе вершина не может соединяться сама с собой, поэтому элементы на главной диагонали матрицы смежности не имеют смысла.
|
||||
- Для неориентированного графа ребра в двух направлениях эквивалентны, поэтому матрица смежности симметрична относительно главной диагонали.
|
||||
- Если заменить в матрице смежности значения $1$ и $0$ на веса, то можно представить и взвешенный граф.
|
||||
|
||||
При представлении графа матрицей смежности мы можем напрямую обращаться к элементам матрицы, чтобы получить информацию о ребрах, поэтому операции добавления, удаления, поиска и изменения обладают высокой эффективностью, равной $O(1)$ . Однако пространственная сложность матрицы равна $O(n^2)$ , поэтому она занимает заметный объем памяти.
|
||||
|
||||
### Список смежности
|
||||
|
||||
<u>Список смежности (adjacency list)</u> использует $n$ связанных списков для представления графа, где узлы списка обозначают вершины. $i$-й список соответствует вершине $i$ и хранит все вершины, смежные с ней, то есть все вершины, соединенные с этой вершиной. На рисунке ниже показан пример графа, представленного списком смежности.
|
||||
|
||||

|
||||
|
||||
Список смежности хранит только реально существующие ребра, а общее число ребер обычно значительно меньше $n^2$ , поэтому этот способ существенно экономит пространство. Однако для поиска ребра в списке смежности нужно проходить по списку, поэтому по времени он уступает матрице смежности.
|
||||
|
||||
Если посмотреть на рисунок выше, можно заметить, что **структура списка смежности очень похожа на "метод цепочек" в хеш-таблице, поэтому для оптимизации эффективности здесь можно использовать сходные идеи**. Например, когда список становится слишком длинным, его можно преобразовать в AVL-дерево или красно-черное дерево, чтобы улучшить временную сложность с $O(n)$ до $O(\log n)$ ; можно также превратить его в хеш-таблицу и снизить сложность до $O(1)$ .
|
||||
|
||||
## Типичные применения графов
|
||||
|
||||
Как показано в таблице ниже, многие реальные системы можно моделировать графами, а соответствующие задачи затем сводить к задачам вычислений на графах.
|
||||
|
||||
<p align="center"> Таблица <id> Распространенные графы в реальной жизни </p>
|
||||
|
||||
| | Вершина | Ребро | Задача вычислений на графе |
|
||||
| -------- | ------- | -------------------- | -------------------------- |
|
||||
| Социальные сети | Пользователь | Дружеская связь | Рекомендация потенциальных друзей |
|
||||
| Линии метро | Станция | Связность между станциями | Рекомендация кратчайшего маршрута |
|
||||
| Солнечная система | Небесное тело | Гравитационное взаимодействие между телами | Вычисление орбит планет |
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,86 @@
|
||||
# Базовые операции графа
|
||||
|
||||
Базовые операции графа можно разделить на операции над "ребрами" и операции над "вершинами". В двух способах представления - "матрица смежности" и "список смежности" - реализация будет различаться.
|
||||
|
||||
## Реализация на основе матрицы смежности
|
||||
|
||||
Пусть дан неориентированный граф с числом вершин $n$ . Тогда способы реализации различных операций показаны на рисунках ниже.
|
||||
|
||||
- **Добавление или удаление ребра**: достаточно изменить соответствующее ребро в матрице смежности, это требует $O(1)$ времени. Поскольку граф неориентированный, нужно одновременно обновлять ребра в обоих направлениях.
|
||||
- **Добавление вершины**: в конец матрицы смежности добавляется одна строка и один столбец, которые полностью заполняются нулями; это требует $O(n)$ времени.
|
||||
- **Удаление вершины**: из матрицы смежности удаляется одна строка и один столбец. В худшем случае, когда удаляются первая строка и первый столбец, приходится "сдвигать вверх-влево" $(n-1)^2$ элементов, поэтому требуется $O(n^2)$ времени.
|
||||
- **Инициализация**: передаются $n$ вершин, затем инициализируется список вершин `vertices` длины $n$ , что требует $O(n)$ времени; после этого инициализируется матрица смежности `adjMat` размера $n \times n$ , что требует $O(n^2)$ времени.
|
||||
|
||||
=== "Инициализация матрицы смежности"
|
||||

|
||||
|
||||
=== "Добавление ребра"
|
||||

|
||||
|
||||
=== "Удаление ребра"
|
||||

|
||||
|
||||
=== "Добавление вершины"
|
||||

|
||||
|
||||
=== "Удаление вершины"
|
||||

|
||||
|
||||
Ниже приведен код реализации графа на основе матрицы смежности:
|
||||
|
||||
```src
|
||||
[file]{graph_adjacency_matrix}-[class]{graph_adj_mat}-[func]{}
|
||||
```
|
||||
|
||||
## Реализация на основе списка смежности
|
||||
|
||||
Пусть неориентированный граф содержит в сумме $n$ вершин и $m$ ребер. Тогда различные операции можно реализовать способом, показанным на рисунках ниже.
|
||||
|
||||
- **Добавление ребра**: достаточно добавить ребро в конец списка, соответствующего вершине; это требует $O(1)$ времени. Поскольку граф неориентированный, нужно одновременно добавлять ребра в обоих направлениях.
|
||||
- **Удаление ребра**: нужно найти и удалить указанное ребро в списке, соответствующем вершине; это требует $O(m)$ времени. В неориентированном графе нужно удалять ребра в обоих направлениях.
|
||||
- **Добавление вершины**: в список смежности добавляется еще один список, а новая вершина становится его головным узлом; это требует $O(1)$ времени.
|
||||
- **Удаление вершины**: требуется пройти по всему списку смежности и удалить все ребра, содержащие указанную вершину; это требует $O(n + m)$ времени.
|
||||
- **Инициализация**: в списке смежности создаются $n$ вершин и $2m$ ребер; это требует $O(n + m)$ времени.
|
||||
|
||||
=== "Инициализация списка смежности"
|
||||

|
||||
|
||||
=== "Добавление ребра"
|
||||

|
||||
|
||||
=== "Удаление ребра"
|
||||

|
||||
|
||||
=== "Добавление вершины"
|
||||

|
||||
|
||||
=== "Удаление вершины"
|
||||

|
||||
|
||||
Ниже приведен код списка смежности. По сравнению с рисунками выше, реальная реализация имеет следующие отличия.
|
||||
|
||||
- Чтобы упростить добавление и удаление вершин, а также упростить код, мы используем список, то есть динамический массив, вместо связного списка.
|
||||
- Для хранения списка смежности используется хеш-таблица, где `key` - это экземпляр вершины, а `value` - список смежных вершин данной вершины.
|
||||
|
||||
Кроме того, в списке смежности мы используем класс `Vertex` для представления вершины. Причина в следующем: если, как и в матрице смежности, различать вершины по индексам списка, то при удалении вершины с индексом $i$ пришлось бы обходить весь список смежности и уменьшать на $1$ все индексы, большие $i$ , что крайне неэффективно. Если же каждая вершина является уникальным экземпляром `Vertex` , то после удаления одной вершины остальные вершины менять уже не требуется.
|
||||
|
||||
```src
|
||||
[file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{}
|
||||
```
|
||||
|
||||
## Сравнение эффективности
|
||||
|
||||
Пусть в графе имеется $n$ вершин и $m$ ребер. В таблице ниже сравниваются временная и пространственная эффективность матрицы смежности и списка смежности. Обрати внимание: список смежности (связный список) соответствует реализации из этой статьи, а список смежности (хеш-таблица) означает вариант, где все списки заменены хеш-таблицами.
|
||||
|
||||
<p align="center"> Таблица <id> Сравнение матрицы смежности и списка смежности </p>
|
||||
|
||||
| | Матрица смежности | Список смежности (связный список) | Список смежности (хеш-таблица) |
|
||||
| ------------ | ----------------- | --------------------------------- | ------------------------------ |
|
||||
| Проверка смежности | $O(1)$ | $O(n)$ | $O(1)$ |
|
||||
| Добавление ребра | $O(1)$ | $O(1)$ | $O(1)$ |
|
||||
| Удаление ребра | $O(1)$ | $O(n)$ | $O(1)$ |
|
||||
| Добавление вершины | $O(n)$ | $O(1)$ | $O(1)$ |
|
||||
| Удаление вершины | $O(n^2)$ | $O(n + m)$ | $O(n)$ |
|
||||
| Занимаемая память | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ |
|
||||
|
||||
Если смотреть только на таблицу, может показаться, что список смежности на основе хеш-таблицы является лучшим и по времени, и по памяти. Но на практике операции над ребрами в матрице смежности часто выполняются быстрее, потому что там нужен лишь один доступ к массиву или одно присваивание. В целом матрица смежности воплощает принцип "обмен пространства на время", а список смежности - принцип "обмен времени на пространство".
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,140 @@
|
||||
# Обход графа
|
||||
|
||||
Дерево представляет отношение "один ко многим", а граф имеет более высокую степень свободы и может выражать произвольные отношения "многие ко многим". Поэтому мы можем рассматривать дерево как частный случай графа. Очевидно, что **операции обхода дерева также являются частным случаем операций обхода графа**.
|
||||
|
||||
И графы, и деревья требуют использования поисковых алгоритмов для реализации обхода. Способы обхода графа также делятся на два типа: <u>обход в ширину</u> и <u>обход в глубину</u>.
|
||||
|
||||
## Обход в ширину
|
||||
|
||||
**Обход в ширину - это способ обхода "от близкого к далекому": начиная с некоторого узла, мы всегда в первую очередь посещаем ближайшие вершины и слой за слоем расширяемся наружу**. Как показано на рисунке ниже, начиная с вершины в левом верхнем углу, мы сначала обходим все смежные вершины этой вершины, затем все смежные вершины следующей вершины и так далее, пока не будут посещены все вершины.
|
||||
|
||||

|
||||
|
||||
### Реализация алгоритма
|
||||
|
||||
BFS обычно реализуется с помощью очереди, код приведен ниже. Очередь обладает свойством "первым пришел - первым вышел", что хорошо соответствует идее BFS "от близкого к далекому".
|
||||
|
||||
1. Поместить стартовую вершину обхода `startVet` в очередь и запустить цикл.
|
||||
2. На каждой итерации цикла извлекать вершину из головы очереди и записывать факт ее посещения, после чего добавлять все смежные вершины этой вершины в хвост очереди.
|
||||
3. Повторять шаг `2.` до тех пор, пока не будут посещены все вершины.
|
||||
|
||||
Чтобы предотвратить повторный обход вершин, нам нужен хеш-набор `visited` , в котором будет записываться, какие узлы уже посещены.
|
||||
|
||||
!!! tip
|
||||
|
||||
Хеш-набор можно рассматривать как хеш-таблицу, которая хранит только `key` и не хранит `value` . Он позволяет выполнять добавление, удаление, поиск и изменение `key` за $O(1)$ времени. Благодаря уникальности `key` хеш-набор обычно используется, например, для устранения повторов.
|
||||
|
||||
```src
|
||||
[file]{graph_bfs}-[class]{}-[func]{graph_bfs}
|
||||
```
|
||||
|
||||
Код сравнительно абстрактен, поэтому рекомендуется сверяться с рисунками ниже для лучшего понимания.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
|
||||
=== "<2>"
|
||||

|
||||
|
||||
=== "<3>"
|
||||

|
||||
|
||||
=== "<4>"
|
||||

|
||||
|
||||
=== "<5>"
|
||||

|
||||
|
||||
=== "<6>"
|
||||

|
||||
|
||||
=== "<7>"
|
||||

|
||||
|
||||
=== "<8>"
|
||||

|
||||
|
||||
=== "<9>"
|
||||

|
||||
|
||||
=== "<10>"
|
||||

|
||||
|
||||
=== "<11>"
|
||||

|
||||
|
||||
!!! question "Является ли последовательность обхода в ширину единственной?"
|
||||
|
||||
Нет. Обход в ширину требует только соблюдения порядка "от близкого к далекому", **а порядок обхода нескольких вершин на одинаковом расстоянии может произвольно меняться**. Например, на рисунке выше можно поменять местами порядок посещения вершин $1$ и $3$ , а также в произвольном порядке переставить вершины $2$, $4$, $6$ .
|
||||
|
||||
### Анализ сложности
|
||||
|
||||
**Временная сложность**: все вершины по одному разу помещаются в очередь и извлекаются из нее, что требует $O(|V|)$ времени; при обходе смежных вершин, поскольку граф неориентированный, все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; в сумме получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: список `res` , хеш-набор `visited` и очередь `que` в худшем случае могут содержать до $|V|$ вершин, поэтому требуется $O(|V|)$ памяти.
|
||||
|
||||
## Обход в глубину
|
||||
|
||||
**Обход в глубину - это способ обхода, при котором сначала идут до самого конца, а когда дальше идти нельзя, откатываются назад**. Как показано на рисунке ниже, начиная с вершины в левом верхнем углу, мы выбираем некоторую смежную вершину текущей вершины, идем до упора, затем возвращаемся назад, снова идем до упора и так далее, пока не будут посещены все вершины.
|
||||
|
||||

|
||||
|
||||
### Реализация алгоритма
|
||||
|
||||
Такой алгоритмический шаблон "дойти до конца и вернуться" обычно реализуется через рекурсию. Подобно обходу в ширину, в обходе в глубину мы также используем хеш-набор `visited` для записи уже посещенных вершин и тем самым избегаем повторного посещения.
|
||||
|
||||
```src
|
||||
[file]{graph_dfs}-[class]{}-[func]{graph_dfs}
|
||||
```
|
||||
|
||||
Алгоритмический процесс обхода в глубину показан на рисунках ниже.
|
||||
|
||||
- **Прямая пунктирная линия обозначает нисходящее рекурсивное развертывание** , то есть запуск нового рекурсивного метода для посещения новой вершины.
|
||||
- **Изогнутая пунктирная линия обозначает обратный возврат по рекурсии** , то есть данный рекурсивный метод завершился и управление вернулось туда, откуда он был вызван.
|
||||
|
||||
Чтобы лучше понять алгоритм, рекомендуется совместить рисунки ниже с кодом и мысленно проследить весь процесс DFS, включая моменты запуска и возврата каждого рекурсивного вызова.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
|
||||
=== "<2>"
|
||||

|
||||
|
||||
=== "<3>"
|
||||

|
||||
|
||||
=== "<4>"
|
||||

|
||||
|
||||
=== "<5>"
|
||||

|
||||
|
||||
=== "<6>"
|
||||

|
||||
|
||||
=== "<7>"
|
||||

|
||||
|
||||
=== "<8>"
|
||||

|
||||
|
||||
=== "<9>"
|
||||

|
||||
|
||||
=== "<10>"
|
||||

|
||||
|
||||
=== "<11>"
|
||||

|
||||
|
||||
!!! question "Является ли последовательность обхода в глубину единственной?"
|
||||
|
||||
Как и в случае обхода в ширину, последовательность DFS тоже не является единственной. Для заданной вершины допустимо сначала углубиться в любое направление, то есть порядок смежных вершин может быть произвольным, и все такие варианты будут корректными обходами в глубину.
|
||||
|
||||
Если взять в качестве примера обход дерева, то варианты "корень $\rightarrow$ лево $\rightarrow$ право", "лево $\rightarrow$ корень $\rightarrow$ право" и "лево $\rightarrow$ право $\rightarrow$ корень" соответствуют прямому, симметричному и обратному обходам соответственно. Они показывают три разных приоритета обхода, но все они относятся к обходу в глубину.
|
||||
|
||||
### Анализ сложности
|
||||
|
||||
**Временная сложность**: все вершины будут посещены по $1$ разу, что требует $O(|V|)$ времени; все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; суммарно получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: число вершин в списке `res` и хеш-наборе `visited` в худшем случае достигает $|V|$ , максимальная глубина рекурсии тоже равна $|V|$ , поэтому требуется $O(|V|)$ памяти.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Графы
|
||||
|
||||

|
||||
|
||||
!!! abstract
|
||||
|
||||
На жизненном пути мы подобны узлам, соединенным бесчисленными невидимыми ребрами.
|
||||
|
||||
Каждая встреча и каждое расставание оставляют в этой огромной сети свой особый след.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Краткие итоги
|
||||
|
||||
### Основные моменты
|
||||
|
||||
- Граф состоит из вершин и ребер и может быть задан как множество вершин и множество ребер.
|
||||
- По сравнению с линейными отношениями (связный список) и отношениями разделения (дерево), сетевые отношения (граф) обладают большей свободой и потому более сложны.
|
||||
- Ребра ориентированного графа имеют направление, в связном графе любые вершины достижимы друг из друга, а в взвешенном графе каждое ребро несет переменную веса.
|
||||
- Матрица смежности использует матрицу для представления графа: каждая строка и каждый столбец соответствуют вершине, а элементы матрицы показывают, есть между двумя вершинами ребро или нет. Матрица смежности очень эффективна для операций добавления, удаления, поиска и изменения, но расходует больше памяти.
|
||||
- Список смежности использует несколько списков для представления графа; $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.
|
||||
|
||||
В этой книге путь рассматривается как последовательность ребер, а не как последовательность вершин. Причина в том, что между двумя вершинами может существовать несколько ребер, и в таком случае каждому ребру соответствует свой путь.
|
||||
|
||||
**Q**: Есть ли в несвязном графе вершины, до которых нельзя дойти?
|
||||
|
||||
В несвязном графе, начиная из некоторой вершины, по крайней мере одна вершина оказывается недостижимой. Чтобы обойти весь несвязный граф, нужно задать несколько стартовых точек и обойти все связные компоненты графа.
|
||||
|
||||
**Q**: Есть ли требования к порядку вершин в списке "всех вершин, соединенных с данной вершиной" в списке смежности?
|
||||
|
||||
Порядок может быть произвольным. Но на практике может понадобиться сортировка по определенному правилу, например по порядку добавления вершин или по возрастанию значений вершин; это помогает быстро находить вершины с некоторым экстремальным свойством.
|
||||