This commit is contained in:
krahets
2026-03-29 02:26:00 +08:00
parent 63276d36d9
commit 37523d4ceb
118 changed files with 74250 additions and 21 deletions

View File

@@ -0,0 +1,103 @@
---
comments: true
---
# 9.1   Граф
<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}
$$
Если рассматривать вершины как узлы, а ребра как ссылки (указатели), соединяющие эти узлы, то граф можно считать структурой данных, выросшей из связного списка. Как показано на рисунке 9-1, **по сравнению с линейными отношениями (связный список) и отношениями разделения (дерево), сетевые отношения (граф) обладают большей свободой** , а потому и сложнее.
![Связь между связным списком, деревом и графом](graph.assets/linkedlist_tree_graph.png){ class="animation-figure" }
<p align="center"> Рисунок 9-1 &nbsp; Связь между связным списком, деревом и графом </p>
## 9.1.1 &nbsp; Распространенные типы и термины графов
В зависимости от того, имеют ли ребра направление, графы делятся на <u>неориентированные графы (undirected graph)</u> и <u>ориентированные графы (directed graph)</u> , как показано на рисунке 9-2.
- В неориентированном графе ребро означает "двустороннюю" связь между двумя вершинами, например отношение "друзья" в WeChat или QQ.
- В ориентированном графе ребро имеет направление, то есть ребра $A \rightarrow B$ и $A \leftarrow B$ независимы друг от друга, как, например, отношения "подписка" и "подписчик" в Weibo или Douyin.
![Ориентированный и неориентированный графы](graph.assets/directed_graph.png){ class="animation-figure" }
<p align="center"> Рисунок 9-2 &nbsp; Ориентированный и неориентированный графы </p>
В зависимости от того, достижимы ли все вершины друг из друга, граф делится на <u>связный граф (connected graph)</u> и <u>несвязный граф (disconnected graph)</u> , как показано на рисунке 9-3.
- В связном графе, начиная из некоторой вершины, можно добраться до любой другой вершины.
- В несвязном графе, начиная из некоторой вершины, по крайней мере одна вершина оказывается недостижимой.
![Связный и несвязный графы](graph.assets/connected_graph.png){ class="animation-figure" }
<p align="center"> Рисунок 9-3 &nbsp; Связный и несвязный графы </p>
Мы также можем добавить к ребрам переменную "вес" и тем самым получить <u>взвешенный граф (weighted graph)</u> , показанный на рисунке 9-4. Например, в мобильных играх вроде Honor of Kings система может вычислять "степень близости" между игроками по времени, проведенному в совместных играх; такую сеть близости можно описать взвешенным графом.
![Взвешенный и невзвешенный графы](graph.assets/weighted_graph.png){ class="animation-figure" }
<p align="center"> Рисунок 9-4 &nbsp; Взвешенный и невзвешенный графы </p>
Для структуры данных "граф" используются следующие распространенные термины.
- <u>Смежность (adjacency)</u>: если между двумя вершинами существует ребро, то эти вершины называются "смежными". На рисунке 9-4 вершинам 2, 3, 5 смежна вершина 1.
- <u>Путь (path)</u>: последовательность ребер, ведущая из вершины A в вершину B, называется "путем" от A до B. На рисунке 9-4 последовательность ребер 1-5-2-4 представляет один из путей от вершины 1 к вершине 4.
- <u>Степень (degree)</u>: число ребер, принадлежащих вершине. Для ориентированного графа <u>входящая степень (in-degree)</u> показывает число ребер, ведущих в вершину, а <u>исходящая степень (out-degree)</u> показывает число ребер, исходящих из вершины.
## 9.1.2 &nbsp; Представление графа
Распространенные способы представления графа включают "матрицу смежности" и "список смежности". Ниже в качестве примера используется неориентированный граф.
### 1. &nbsp; Матрица смежности
Пусть число вершин графа равно $n$ ; тогда <u>матрица смежности (adjacency matrix)</u> использует матрицу размера $n \times n$ для представления графа: каждая строка и каждый столбец соответствуют вершине, а элементы матрицы отражают наличие ребра, то есть показывают, существует между двумя вершинами связь или нет.
Как показано на рисунке 9-5, пусть матрица смежности обозначается как $M$ , а список вершин - как $V$ ; тогда элемент матрицы $M[i, j] = 1$ означает, что между вершинами $V[i]$ и $V[j]$ существует ребро, а элемент $M[i, j] = 0$ означает, что ребра между ними нет.
![Представление графа матрицей смежности](graph.assets/adjacency_matrix.png){ class="animation-figure" }
<p align="center"> Рисунок 9-5 &nbsp; Представление графа матрицей смежности </p>
Матрица смежности обладает следующими особенностями.
- В простом графе вершина не может соединяться сама с собой, поэтому элементы на главной диагонали матрицы смежности не имеют смысла.
- Для неориентированного графа ребра в двух направлениях эквивалентны, поэтому матрица смежности симметрична относительно главной диагонали.
- Если заменить в матрице смежности значения $1$ и $0$ на веса, то можно представить и взвешенный граф.
При представлении графа матрицей смежности мы можем напрямую обращаться к элементам матрицы, чтобы получить информацию о ребрах, поэтому операции добавления, удаления, поиска и изменения обладают высокой эффективностью, равной $O(1)$ . Однако пространственная сложность матрицы равна $O(n^2)$ , поэтому она занимает заметный объем памяти.
### 2. &nbsp; Список смежности
<u>Список смежности (adjacency list)</u> использует $n$ связанных списков для представления графа, где узлы списка обозначают вершины. $i$-й список соответствует вершине $i$ и хранит все вершины, смежные с ней, то есть все вершины, соединенные с этой вершиной. На рисунке 9-6 показан пример графа, представленного списком смежности.
![Представление графа списком смежности](graph.assets/adjacency_list.png){ class="animation-figure" }
<p align="center"> Рисунок 9-6 &nbsp; Представление графа списком смежности </p>
Список смежности хранит только реально существующие ребра, а общее число ребер обычно значительно меньше $n^2$ , поэтому этот способ существенно экономит пространство. Однако для поиска ребра в списке смежности нужно проходить по списку, поэтому по времени он уступает матрице смежности.
Если посмотреть на рисунок 9-6, можно заметить, что **структура списка смежности очень похожа на "метод цепочек" в хеш-таблице, поэтому для оптимизации эффективности здесь можно использовать сходные идеи**. Например, когда список становится слишком длинным, его можно преобразовать в AVL-дерево или красно-черное дерево, чтобы улучшить временную сложность с $O(n)$ до $O(\log n)$ ; можно также превратить его в хеш-таблицу и снизить сложность до $O(1)$ .
## 9.1.3 &nbsp; Типичные применения графов
Как показано в таблице 9-1, многие реальные системы можно моделировать графами, а соответствующие задачи затем сводить к задачам вычислений на графах.
<p align="center"> Таблица 9-1 &nbsp; Распространенные графы в реальной жизни </p>
<div class="center-table" markdown>
| | Вершина | Ребро | Задача вычислений на графе |
| -------- | ------- | -------------------- | -------------------------- |
| Социальные сети | Пользователь | Дружеская связь | Рекомендация потенциальных друзей |
| Линии метро | Станция | Связность между станциями | Рекомендация кратчайшего маршрута |
| Солнечная система | Небесное тело | Гравитационное взаимодействие между телами | Вычисление орбит планет |
</div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
---
comments: true
icon: material/graphql
---
# Глава 9. &nbsp; Графы
![Графы](../assets/covers/chapter_graph.jpg){ class="cover-image" }
!!! abstract
На жизненном пути мы подобны узлам, соединенным бесчисленными невидимыми ребрами.
Каждая встреча и каждое расставание оставляют в этой огромной сети свой особый след.
## Содержание главы
- [9.1 &nbsp; Граф](graph.md)
- [9.2 &nbsp; Базовые операции над графами](graph_operations.md)
- [9.3 &nbsp; Обход графа](graph_traversal.md)
- [9.4 &nbsp; Резюме](summary.md)

View File

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