> For the complete documentation index, see [llms.txt](https://op-al.gitbook.io/s-30-voprosy-i-dop.-voprosy/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://op-al.gitbook.io/s-30-voprosy-i-dop.-voprosy/26.-dinamicheskie-struktury-dannykh.-grafy.-algoritmy-obkhoda-grafa-v-shirinu-i-glubinu.-primery..md).

# 26. Динамические структуры данных. Графы. Алгоритмы обхода графа в ширину и глубину. Примеры.

## **Динамическая структура данных**&#x20;

* Это структуры данных, память под которые выделяется и освобождается по мере необходимости. Примером таких структур можно считать деревья, списки, очередь, стек.

## Граф

Это абстрактный тип данных, который предназначен для реализации понятий неориентированного и ориентированного графа из области теории графов в математике.&#x20;

**Структура данных графа** состоит из конечного набора вершин вместе с набором неупорядоченных пар этих вершин для неориентированного графа или набором упорядоченных пар для ориентированного графа.

## Область применения графов

* Геолокация
* Планирование процессов
* Компьютерные сети
* Структура программы
* Химические соединения
* Связи между людьми

## Основные понятия теории графов

**Ориентированный граф** - G(V, E) - пара из V(конечного множества) и Е(подмножества декартового произведения множества V\*V).

**Неориентированный граф** - ребра есть неупорядоченные пары. Если для любой пары вершин существует путь, граф неориентированный.

**Взвешенный граф** - граф, в котором каждому ребру приписан вес. С(U, V)

<figure><img src="https://3644367790-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUZOUsWlbTNqsUYoinAff%2Fuploads%2FOPtTxi6ADn0SWZP9iIJt%2Fimage.png?alt=media&amp;token=bf5ca9df-545a-4a44-a7cb-bac2572f2121" alt=""><figcaption><p>Взвешенный граф</p></figcaption></figure>

**Вершины графа** - элементы множества V.

**Ребра графа** - элементы множества Е.

**Петля** - ребро из вершины Vi в само себя.

Вершины Vi и Vj **смежны**, если между ними имеется связь (Vi, Vj).

**Путем** из V0 в Vn называется последовательность рёбер таких, что Е1 = (V0, V1), E2 = (V1, V2)... En = (Vn-1, Vn)

**Простым путем** называется путь, в котором все вершины попарно различны.

**Длина пути** - количество рёбер в пути.

**Цикл** - путь, в котором V0=Vn.&#x20;

**Связанная компонента вершины V** - множество вершин неориентированного графа до которых существует путь из V.

## Обход графа. Поиск в ширину(BFS)

Поиск в ширину от исходной вершины - просмотр всех вершин графа в порядке возрастания расстояния от исходной вершины.

Алгоритм работает следующим образом:

1. Начните с размещения любой вершины графа в конце очереди.
2. Возьмите передний элемент очереди и добавьте его в список посещенных.
3. Создайте список смежных узлов этой вершины. Добавьте те, которых нет в списке посещенных, в конец очереди.
4. Продолжайте повторять шаги 2 и 3, пока очередь не опустеет.

### Пример BFS

Давайте посмотрим, как алгоритм «поиска в ширину» работает на примере. Мы используем неориентированный граф с 5 вершинами.

[![](https://evileg.com/media/users/mafulechka/photos/photo_RJfYSTQ.jpg)](https://evileg.com/users/mafulechka/albums/photo/758/)

Мы начнем с вершины 0, алгоритм BFS начинается с помещения его в список посещенных и размещения всех смежных вершин в стеке.

[![](https://evileg.com/media/users/mafulechka/photos/photo_KW8Kfe5.jpg)](https://evileg.com/users/mafulechka/albums/photo/759/)

Затем мы посещаем элемент в начале очереди, то есть 1, и переходим к соседним узлам. Так как 0 уже был посещен, мы посещаем 2.

[![](https://evileg.com/media/users/mafulechka/photos/photo_vDoSuVQ.jpg)](https://evileg.com/users/mafulechka/albums/photo/760/)

У вершины 2 есть соседняя не посещенная вершина 4, поэтому мы добавляем ее в конец очереди и посещаем 3, которая находится в начале очереди.

[![](https://evileg.com/media/users/mafulechka/photos/photo_t3r5vs8.jpg)](https://evileg.com/users/mafulechka/albums/photo/761/)

[![](https://evileg.com/media/users/mafulechka/photos/photo_xKZ9aUO.jpg)](https://evileg.com/users/mafulechka/albums/photo/762/)

В очереди остается только 4, поскольку единственный соседний узел с 3, то есть 0, уже посещен. Мы посещаем вершину 4.

[![](https://evileg.com/media/users/mafulechka/photos/photo_XsnjTzJ.jpg)](https://evileg.com/users/mafulechka/albums/photo/763/)

Поскольку очередь пуста, мы завершили обход в ширину графика.

### Особенности алгоритма

1. Каждая вершина обрабатывается не более 1 раза
2. Легко найти кратчайший путь
3. Легко найти обратный путь

**Сложность алгоритма:** О(|V|+|E|)

Алгоритм реализован на псевдокоде. Для реализации на Си смотрите[ здесь.](https://evileg.com/ru/post/512/)

```
create a queue Q 
mark v as visited and put v into Q 
while Q is non-empty 
    remove the head u of Q 
    mark and enqueue all (unvisited) neighbours of u
```

## Обход в глубину (Depth-First Search, DFS)

Список смежности - это один из способов представления графа:

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/fe9/a14/fae/fe9a14fae02385ffb95d058351b7fe51.jpg" alt="список смежности для данного графа" height="200" width="626"><figcaption><p>список смежности для данного графа</p></figcaption></figure>

Для каждой вершины (первый столбик) мы составляем список смежных ей вершин, то есть список вершин с которыми у данной есть общие ребра(ребро инцидентное данным вершинам).

### Алгоритм поиска

Начнем мы с вершины “0”. В первую очередь алгоритм поиска в глубину поместит ее саму в список “Пройденные” (на изображении “Visited”), а ее смежные вершины — в стек.

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/107/8ff/d8e/1078ffd8eeb4ef92d4c73d68041192b9.png" alt="Выберите элемент (вершину) и поместите его в список “Пройденные”." height="484" width="1460"><figcaption><p>Выберите элемент (вершину) и поместите его в список “Пройденные”.</p></figcaption></figure>

Затем мы берем следующий элемент сверху стека, т.е. к вершину “1”, и переходим к ее соседним вершинам. Поскольку вершина “0” уже пройдена, следующая вершина “2”.

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/b86/0a3/595/b860a3595fc4309f88efcd3f97fc9fa9.png" alt="Обход элемента на вершине стека." height="484" width="1460"><figcaption><p>Обход элемента на вершине стека.</p></figcaption></figure>

Вершина “2” смежна непройденной вершине “4”, следовательно мы добавляем ее наверх стека и проходим ее.

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/3bc/8f7/19c/3bc8f719c5419daaee0fd4e0b4cbcf08.png" alt="Вершина “2” смежна непройденной вершине “4”, следовательно мы помещаем ее в верх стека." height="484" width="1460"><figcaption><p>Вершина “2” смежна непройденной вершине “4”, следовательно мы помещаем ее в верх стека.</p></figcaption></figure>

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/7f8/c20/d7b/7f8c20d7b37e96963c75da3890c7d2c0.png" alt="Добавляем вершину “4” в список “Пройденные” после прохождения." height="484" width="1460"><figcaption><p>Добавляем вершину “4” в список “Пройденные” после прохождения.</p></figcaption></figure>

После того, как мы пройдем последний элемент (вершину “3”), в стеке не останется непройденных смежных вершин, и таким образом мы завершили обход графа в глубину.

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/283/0eb/b7e/2830ebb7e746d44d79f6c6ad8fa373f3.png" alt="После проверки всех смежных вершин для вершины “3” стек остался пустым, а значит алгоритм обхода графа в глубину завершил свою работу." height="484" width="1460"><figcaption><p>После проверки всех смежных вершин для вершины “3” стек остался пустым, а значит алгоритм обхода графа в глубину завершил свою работу.</p></figcaption></figure>

:star: Код реализован на С++! Реализован только сам алгоритм, полный код [здесь.](https://habr.com/ru/companies/otus/articles/660725/)

<pre class="language-cpp"><code class="lang-cpp"><strong>
</strong>void Graph::DFS(int vertex) {
  visited[vertex] = true;
  list&#x3C;int> adjList = adjLists[vertex];
 
  cout &#x3C;&#x3C; vertex &#x3C;&#x3C; " ";
 
  list&#x3C;int>::iterator i;
  for (i = adjList.begin(); i != adjList.end(); ++i)
    if (!visited[*i])
      DFS(*i);
}
</code></pre>
