mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-15 21:20:58 +00:00
build
This commit is contained in:
@@ -4,13 +4,13 @@ comments: true
|
||||
|
||||
# 9.3 Обход графа
|
||||
|
||||
Дерево представляет отношение "один ко многим", а граф имеет более высокую степень свободы и может выражать произвольные отношения "многие ко многим". Поэтому мы можем рассматривать дерево как частный случай графа. Очевидно, что **операции обхода дерева также являются частным случаем операций обхода графа**.
|
||||
Дерево представляет отношение "один ко многим", тогда как граф обладает большей свободой и может выражать произвольные отношения "многие ко многим". Поэтому дерево можно рассматривать как частный случай графа. Очевидно, что **операции обхода дерева также являются частным случаем операций обхода графа**.
|
||||
|
||||
И графы, и деревья требуют использования поисковых алгоритмов для реализации обхода. Способы обхода графа также делятся на два типа: <u>обход в ширину</u> и <u>обход в глубину</u>.
|
||||
И графы, и деревья требуют применения алгоритмов обхода. Способы обхода графа также делятся на два типа: <u>обход в ширину</u> и <u>обход в глубину</u>.
|
||||
|
||||
## 9.3.1 Обход в ширину
|
||||
|
||||
**Обход в ширину - это способ обхода "от близкого к далекому": начиная с некоторого узла, мы всегда в первую очередь посещаем ближайшие вершины и слой за слоем расширяемся наружу**. Как показано на рисунке 9-9, начиная с вершины в левом верхнем углу, мы сначала обходим все смежные вершины этой вершины, затем все смежные вершины следующей вершины и так далее, пока не будут посещены все вершины.
|
||||
**Обход в ширину - это способ обхода от ближнего к дальнему, при котором начиная с некоторого узла сначала посещают ближайшие вершины, а затем слой за слоем расширяются наружу**. Как показано на рисунке 9-9, начиная с вершины в левом верхнем углу, мы сначала обходим все смежные вершины этой вершины, затем все смежные вершины следующей вершины и так далее, пока не будут посещены все вершины.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -18,17 +18,17 @@ comments: true
|
||||
|
||||
### 1. Реализация алгоритма
|
||||
|
||||
BFS обычно реализуется с помощью очереди, код приведен ниже. Очередь обладает свойством "первым пришел - первым вышел", что хорошо соответствует идее BFS "от близкого к далекому".
|
||||
BFS обычно реализуется с помощью очереди, код приведен ниже. Очередь обладает свойством "первым пришел - первым вышел", что хорошо соответствует идее BFS "от ближнего к дальнему".
|
||||
|
||||
1. Поместить стартовую вершину обхода `startVet` в очередь и запустить цикл.
|
||||
2. На каждой итерации цикла извлекать вершину из головы очереди и записывать факт ее посещения, после чего добавлять все смежные вершины этой вершины в хвост очереди.
|
||||
2. На каждой итерации цикла извлекать вершину из головы очереди и записывать ее посещение, после чего добавлять все смежные вершины этой вершины в хвост очереди.
|
||||
3. Повторять шаг `2.` до тех пор, пока не будут посещены все вершины.
|
||||
|
||||
Чтобы предотвратить повторный обход вершин, нам нужен хеш-набор `visited` , в котором будет записываться, какие узлы уже посещены.
|
||||
Чтобы предотвратить повторный обход вершин, нам нужно хеш-множество `visited` , в котором записывается, какие вершины уже посещены.
|
||||
|
||||
!!! tip
|
||||
|
||||
Хеш-набор можно рассматривать как хеш-таблицу, которая хранит только `key` и не хранит `value` . Он позволяет выполнять добавление, удаление, поиск и изменение `key` за $O(1)$ времени. Благодаря уникальности `key` хеш-набор обычно используется, например, для устранения повторов.
|
||||
Хеш-множество можно рассматривать как хеш-таблицу, которая хранит только `key` и не хранит `value` . Оно позволяет выполнять добавление, удаление и проверку наличия `key` за $O(1)$ времени. Благодаря уникальности `key` хеш-множество обычно используется, например, для устранения повторов.
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -519,17 +519,17 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
!!! question "Является ли последовательность обхода в ширину единственной?"
|
||||
|
||||
Нет. Обход в ширину требует только соблюдения порядка "от близкого к далекому", **а порядок обхода нескольких вершин на одинаковом расстоянии может произвольно меняться**. Например, на рисунке 9-10 можно поменять местами порядок посещения вершин $1$ и $3$ , а также в произвольном порядке переставить вершины $2$, $4$, $6$ .
|
||||
Нет. Обход в ширину требует только соблюдения порядка "от ближнего к дальнему", **а порядок обхода нескольких вершин на одинаковом расстоянии может произвольно меняться**. Например, на рисунке 9-10 можно поменять местами порядок посещения вершин $1$ и $3$ , а вершины $2$, $4$, $6$ также можно переставлять произвольно.
|
||||
|
||||
### 2. Анализ сложности
|
||||
|
||||
**Временная сложность**: все вершины по одному разу помещаются в очередь и извлекаются из нее, что требует $O(|V|)$ времени; при обходе смежных вершин, поскольку граф неориентированный, все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; в сумме получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: список `res` , хеш-набор `visited` и очередь `que` в худшем случае могут содержать до $|V|$ вершин, поэтому требуется $O(|V|)$ памяти.
|
||||
**Пространственная сложность**: список `res` , хеш-множество `visited` и очередь `que` в худшем случае могут содержать до $|V|$ вершин, поэтому требуется $O(|V|)$ памяти.
|
||||
|
||||
## 9.3.2 Обход в глубину
|
||||
|
||||
**Обход в глубину - это способ обхода, при котором сначала идут до самого конца, а когда дальше идти нельзя, откатываются назад**. Как показано на рисунке 9-11, начиная с вершины в левом верхнем углу, мы выбираем некоторую смежную вершину текущей вершины, идем до упора, затем возвращаемся назад, снова идем до упора и так далее, пока не будут посещены все вершины.
|
||||
**Обход в глубину - это способ обхода, при котором сначала идут до самого конца, а когда дальше идти нельзя, возвращаются назад**. Как показано на рисунке 9-11, начиная с вершины в левом верхнем углу, мы выбираем некоторую смежную вершину текущей вершины, идем до упора, затем возвращаемся назад, снова идем до упора и так далее, пока не будут посещены все вершины.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -537,7 +537,7 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
### 1. Реализация алгоритма
|
||||
|
||||
Такой алгоритмический шаблон "дойти до конца и вернуться" обычно реализуется через рекурсию. Подобно обходу в ширину, в обходе в глубину мы также используем хеш-набор `visited` для записи уже посещенных вершин и тем самым избегаем повторного посещения.
|
||||
Такой алгоритмический шаблон "дойти до конца и вернуться" обычно реализуется через рекурсию. Подобно обходу в ширину, в обходе в глубину мы также используем хеш-множество `visited` для записи уже посещенных вершин и тем самым избегаем повторного посещения.
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -942,8 +942,8 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
Алгоритмический процесс обхода в глубину показан на рисунках ниже.
|
||||
|
||||
- **Прямая пунктирная линия обозначает нисходящее рекурсивное развертывание** , то есть запуск нового рекурсивного метода для посещения новой вершины.
|
||||
- **Изогнутая пунктирная линия обозначает обратный возврат по рекурсии** , то есть данный рекурсивный метод завершился и управление вернулось туда, откуда он был вызван.
|
||||
- **Прямая пунктирная линия обозначает нисходящую рекурсию** , то есть запуск нового рекурсивного метода для посещения новой вершины.
|
||||
- **Изогнутая пунктирная линия обозначает восходящую рекурсию** , то есть данный рекурсивный метод завершился и управление вернулось туда, откуда он был вызван.
|
||||
|
||||
Чтобы лучше понять алгоритм, рекомендуется совместить рисунки ниже с кодом и мысленно проследить весь процесс DFS, включая моменты запуска и возврата каждого рекурсивного вызова.
|
||||
|
||||
@@ -992,4 +992,4 @@ BFS обычно реализуется с помощью очереди, код
|
||||
|
||||
**Временная сложность**: все вершины будут посещены по $1$ разу, что требует $O(|V|)$ времени; все ребра будут посещены по $2$ раза, что требует $O(2|E|)$ времени; суммарно получается $O(|V| + |E|)$ .
|
||||
|
||||
**Пространственная сложность**: число вершин в списке `res` и хеш-наборе `visited` в худшем случае достигает $|V|$ , максимальная глубина рекурсии тоже равна $|V|$ , поэтому требуется $O(|V|)$ памяти.
|
||||
**Пространственная сложность**: число вершин в списке `res` и хеш-множестве `visited` в худшем случае достигает $|V|$ , максимальная глубина рекурсии тоже равна $|V|$ , поэтому требуется $O(|V|)$ памяти.
|
||||
|
||||
Reference in New Issue
Block a user