Как найти количество кратчайших путей графе

Базовые алгоритмы нахождения кратчайших путей во взвешенных графах

Время на прочтение
5 мин

Количество просмотров 240K

Наверняка многим из гейм-девелоперов (или просто людям, увлекающимися програмировагнием) будет интересно услышать эти четыре важнейших алгоритма, решающих задачи о кратчайших путях.

Сформулируем определения и задачу.
Графом будем называть несколько точек (вершин), некоторые пары которых соединены отрезками (рёбрами). Граф связный, если от каждой вершины можно дойти до любой другой по этим отрезкам. Циклом назовём какой-то путь по рёбрам графа, начинающегося и заканчивающегося в одной и той же вершине. И ещё граф называется взвешенным, если каждому ребру соответствует какое-то число (вес). Не может быть двух рёбер, соединяющих одни и те же вершины.
Каждый из алгоритмов будет решать какую-то задачу о кратчайших путях на взвешенном связном. Кратчайший путь из одной вершины в другую — это такой путь по рёбрам, что сумма весов рёбер, по которым мы прошли будет минимальна.
Для ясности приведу пример такой задачи в реальной жизни. Пусть, в стране есть несколько городов и дорог, соединяющих эти города. При этом у каждой дороги есть длина. Вы хотите попасть из одного города в другой, проехав как можно меньший путь.

Считаем, что в графе n вершин и m рёбер.
Пойдём от простого к сложному.

Алгоритм Флойда-Уоршелла

Находит расстояние от каждой вершины до каждой за количество операций порядка n^3. Веса могут быть отрицательными, но у нас не может быть циклов с отрицательной суммой весов рёбер (иначе мы можем ходить по нему сколько душе угодно и каждый раз уменьшать сумму, так не интересно).
В массиве d[0… n — 1][0… n — 1] на i-ой итерации будем хранить ответ на исходную задачу с ограничением на то, что в качестве «пересадочных» в пути мы будем использовать вершины с номером строго меньше i — 1 (вершины нумеруем с нуля). Пусть идёт i-ая итерация, и мы хотим обновить массив до i + 1-ой. Для этого для каждой пары вершин просто попытаемся взять в качестве пересадочной i — 1-ую вершину, и если это улучшает ответ, то так и оставим. Всего сделаем n + 1 итерацию, после её завершения в качестве «пересадочных» мы сможем использовать любую, и массив d будет являться ответом.
n итераций по n итераций по n итераций, итого порядка n^3 операций.
Псевдокод:

прочитать g // g[0 ... n - 1][0 ... n - 1] - массив, в котором хранятся веса рёбер, g[i][j] = 2000000000, если ребра между i и j нет
d = g
for i = 1 ... n + 1
     for j = 0 ... n - 1
          for k = 0 ... n - 1
              if d[j][k] > d[j][i - 1] + d[i - 1][k]
                  d[j][k] = d[j][i - 1] + d[i - 1][k]
вывести d

Алгоритм Форда-Беллмана

Находит расстояние от одной вершины (дадим ей номер 0) до всех остальных за количество операций порядка n * m. Аналогично предыдущему алгоритму, веса могут быть отрицательными, но у нас не может быть циклов с отрицательной суммой весов рёбер.
Заведём массив d[0… n — 1], в котором на i-ой итерации будем хранить ответ на исходную задачу с ограничением на то, что в путь должно входить строго меньше i рёбер. Если таких путей до вершины j нет, то d[j] = 2000000000 (это должна быть какая-то недостижимая константа, «бесконечность»). В самом начале d заполнен 2000000000. Чтобы обновлять на i-ой итерации массив, надо просто пройти по каждому ребру и попробовать улучшить расстояние до вершин, которые оно соединяет. Кратчайшие пути не содержат циклов, так как все циклы неотрицательны, и мы можем убрать цикл из путя, при этом длина пути не ухудшится (хочется также отметить, что именно так можно найти отрицательные циклы в графе: надо сделать ещё одну итерацию и посмотреть, не улучшилось ли расстояние до какой-нибудь вершины). Поэтому длина кратчайшего пути не больше n — 1, значит, после n-ой итерации d будет ответом на задачу.
n итераций по m итераций, итого порядка n * m операций.
Псевдокод:

прочитать e // e[0 ... m - 1] - массив, в котором хранятся рёбра и их веса (first, second - вершины, соединяемые ребром, value - вес ребра)
for i = 0 ... n - 1
    d[i] = 2000000000
d[0] = 0
for i = 1 ... n
    for j = 0 ... m - 1
        if d[e[j].second] > d[e[j].first] + e[j].value
            d[e[j].second] = d[e[j].first] + e[j].value
        if d[e[j].first] > d[e[j].second] + e[j].value
            d[e[j].first] = d[e[j].second] + e[j].value
вывести d

Алгоритм Дейкстры

Находит расстояние от одной вершины (дадим ей номер 0) до всех остальных за количество операций порядка n^2. Все веса неотрицательны.
На каждой итерации какие-то вершины будут помечены, а какие-то нет. Заведём два массива: mark[0… n — 1] — True, если вершина помечена, False иначе, d[0… n — 1] — для каждой вершины будет храниться длина кратчайшего пути, проходящего только по помеченным вершинам в качестве «пересадочных». Также поддерживается инвариант того, что для помеченных вершин длина, указанная в d, и есть ответ. Сначала помечена только вершина 0, а g[i] равно x, если 0 и i соединяет ребро весом x, равно 2000000000, если их не соединяет ребро, и равно 0, если i = 0.
На каждой итерации мы находим вершину, с наименьшим значением в d среди непомеченных, пусть это вершина v. Тогда значение d[v] является ответом для v. Докажем. Пусть, кратчайший путь до v из 0 проходит не только по помеченным вершинам в качестве «пересадочных», и при этом он короче d[v]. Возьмём первую встретившуюся непомеченную вершину на этом пути, назовём её u. Длина пройденной части пути (от 0 до u) — d[u]. len >= d[u], где len — длина кратчайшего пути из 0 до v (т. к. отрицательных рёбер нет), но по нашему предположению len меньше d[v]. Значит, d[v] > len >= d[u]. Но тогда v не подходит под своё описание — у неё не наименьшее значение d[v] среди непомеченных. Противоречие.
Теперь смело помечаем вершину v и пересчитываем d. Так делаем, пока все вершины не станут помеченными, и d не станет ответом на задачу.
n итераций по n итераций (на поиск вершины v), итого порядка n^2 операций.
Псевдокод:

прочитать g // g[0 ... n - 1][0 ... n - 1] - массив, в котором хранятся веса рёбер, g[i][j] = 2000000000, если ребра между i и j нет
d = g
d[0] = 0
mark[0] = True
for i = 1 ... n - 1
    mark[i] = False
for i = 1 ... n - 1
    v = -1
    for i = 0 ... n - 1
        if (not mark[i]) and ((v == -1) or (d[v] > d[i]))
            v = i
    mark[v] = True
    for i = 0 ... n - 1
        if d[i] > d[v] + g[v][i]
            d[i] = d[v] + g[v][i]
вывести d

Алгоритм Дейкстры для разреженных графов

Делает то же самое, что и алгоритм Дейкстры, но за количество операций порядка m * log(n). Следует заметить, что m может быть порядка n^2, то есть эта вариация алгоритма Дейкстры не всегда быстрее классической, а только при маленьких m.
Что нам нужно в алгоритме Дейкстры? Нам нужно уметь находить по значению d минимальную вершину и уметь обновлять значение d в какой-то вершине. В классической реализации мы пользуемся простым массивом, находить минимальную по d вершину мы можем за порядка n операций, а обновлять — за 1 операцию. Воспользуемся двоичной кучей (во многих объектно-ориентированных языках она встроена). Куча поддерживает операции: добавить в кучу элемент (за порядка log(n) операций), найти минимальный элемент (за 1 операцию), удалить минимальный элемент (за порядка log(n) операций), где n — количество элементов в куче.
Создадим массив d[0… n — 1] (его значение то же самое, что и раньше) и кучу q. В куче будем хранить пары из номера вершины v и d[v] (сравниваться пары должны по d[v]). Также в куче могут быть фиктивные элементы. Так происходит, потому что значение d[v] обновляется, но мы не можем изменить его в куче. Поэтому в куче могут быть несколько элементов с одинаковым номером вершины, но с разным значением d (но всего вершин в куче будет не более m, я гарантирую это). Когда мы берём минимальное значение в куче, надо проверить, является ли этот элемент фиктивным. Для этого достаточно сравнить значение d в куче и реальное его значение. А ещё для записи графа вместо двоичного массива используем массив списков.
m раз добавляем элемент в кучу, получаем порядка m * log(n) операций.
Псевдокод:

прочитать g // g[0 ... n - 1] - массив списков, в i-ом списке хранятся пары: first - вершина, соединённая с i-ой вершиной ребром, second - вес этого ребра
d[0] = 0
for i = 0 ... n - 1
    d[i] = 2000000000
for i in g[0] // python style
    d[i.first] = i.second
    q.add(pair(i.second, i.first))
for i = 1 ... n - 1
    v = -1
    while (v = -1) or (d[v] != val)
        v = q.top.second
        val = q.top.first
    q.removeTop
    mark[v] = true
    for i in g[v]
        if d[i.first] > d[v] + i.second
            d[i.first] = d[v] + i.second
            q.add(pair(d[i.first], i.first))
вывести d

Автор — Лада Борисовна Есакова.

Подсчет путей в ориентированном графе. ЗАДАЧА № 15.

В этой задаче требуется подсчитать количество путей, ведущих из одной вершины графа в другую. Обычно задачу решают преобразованием графа в дерево. Однако, при сложной структуре графа такое решение становится очень трудоемким. Велика вероятность ошибки.

Рассмотрим простой и эффективный способ решения.

В этой задаче мы имеем дело с ориентированным графом (графом, у которого ребра имеют направление). Т.е. ребра имеют вид стрелок. Две вершины, соединенные напрямую стрелкой, называются смежными. Вершина, из которой выходит стрелка, называется предком, а вершина, в которую входит стрелка – потомком.

Несложно понять, что количество путей, которыми можно попасть в некоторую вершину, равно сумме количеств путей предков этой вершины.

Пример:

На рисунке – схема дорог, связывающих города А, Б, В, Г, Д, Е, Ж, З, И, К. По каждой дороге можно двигаться только в одном направлении, указанном стрелкой. Сколько существует различных путей из города А в город Ж?

1
Решение:

Каждой вершине, начиная с начальной (A), поставим в соответствие индекс, равный количеству путей, которыми можно попасть в эту вершину. Для вершины A (начало пути) индекс всегда равен 1 (в начало пути можно попасть единственным образом – никуда не двигаясь). Теперь сформулируем правило: индекс вершины равен сумме индексов его предков. Исходя из этого индекс Б равен 1 (предок у Б один – вершина A).

У вершины Д предками являются А и Б, значит индекс вершины Д равен 1+1=2.

2

Очевидно, что мы можем посчитать индекс только тех вершин, индексы предков которых уже посчитаны. Например, мы не можем посчитать индекс Г, пока не посчитан индекс В. Двигаясь последовательно, мы рассчитаем индексы всех вершин.

Индекс вершины Ж и будет ответом задачи.

3
Ответ: 11

Спасибо за то, что пользуйтесь нашими публикациями.
Информация на странице «Задача №15. Графы. Поиск количества путей.» подготовлена нашими авторами специально, чтобы помочь вам в освоении предмета и подготовке к ЕГЭ и ОГЭ.
Чтобы успешно сдать нужные и поступить в высшее учебное заведение или техникум нужно использовать все инструменты: учеба, контрольные, олимпиады, онлайн-лекции, видеоуроки, сборники заданий.
Также вы можете воспользоваться другими материалами из разделов нашего сайта.

Публикация обновлена:
07.05.2023

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    Given an unweighted directed graph, can be cyclic or acyclic. Print the number of shortest paths from a given vertex to each of the vertices. For example consider the below graph. There is one shortest path vertex 0 to vertex 0 (from each vertex there is a single shortest path to itself), one shortest path between vertex 0 to vertex 2 (0->2), and there are 4 different shortest paths from vertex 0 to vertex 6: 

    1. 0->1->3->4->6 
    2. 0->1->3->5->6 
    3. 0->2->3->4->6 
    4. 0->2->3->5->6 

    The idea is to use BFS. We use two arrays called dist[] and paths[], dist[] represents the shortest distances from source vertex, and paths[] represents the number of different shortest paths from the source vertex to each of the vertices. Initially all the elements in dist[] are infinity except source vertex which is equal to 0, since the distance to source vertex from itself is 0, and all the elements in paths[] are 0 except source vertex which is equal to 1, since each vertex has a single shortest path to itself. after that, we start traversing the graph using BFS manner. 

    Then, for every neighbor Y of each vertex X do: 

    1. if dist[Y] > dist[X]+1 decrease the dist[Y] to dist[X] +1 and assign the number of paths of vertex X to number of paths of vertex Y. 
    2. else if dist[Y] = dist[X] + 1, then add the number of paths of vertex X to the number of paths of vertex Y. 

    For example: 

    Let’s take a look at the below graph. The source vertex is 0. Suppose we traverse on vertex 2, we check all its neighbors, which is only 3.since vertex 3 was already visited when we were traversed vertex 1, dist[3] = 2 and paths[3] = 1. The second condition is true, so it means that additional shortest paths have been found, so we add to the number of paths of vertex 3, the number of paths of vertex 2. 

    The equal condition happens when we traverse on vertex 5: 

    Implementation:

    C++

    #include <bits/stdc++.h>

    using namespace std;

    void BFS(vector<int> adj[], int src, int dist[],

                               int paths[], int n)

    {

        bool visited[n];

        for (int i = 0; i < n; i++)

            visited[i] = false;

        dist[src] = 0;

        paths[src] = 1;

        queue <int> q;

        q.push(src);

        visited[src] = true;

        while (!q.empty())

        {

            int curr = q.front();

            q.pop();

            for (auto x : adj[curr])

            {

                if (visited[x] == false)

                {

                    q.push(x);

                    visited[x] = true;

                }

                if (dist[x] > dist[curr] + 1)

                {

                    dist[x] = dist[curr] + 1;

                    paths[x] = paths[curr];

                }

                else if (dist[x] == dist[curr] + 1)

                    paths[x] += paths[curr];

            }

        }

    }

    void findShortestPaths(vector<int> adj[],

                           int s, int n)

    {

        int dist[n], paths[n];

        for (int i = 0; i < n; i++)

            dist[i] = INT_MAX;

        for (int i = 0; i < n; i++)

            paths[i] = 0;

        BFS(adj, s, dist, paths, n);

        cout << "Numbers of shortest Paths are: ";

        for (int i = 0; i < n; i++)

            cout << paths[i] << " ";

    }

    void addEdge(vector<int> adj[], int u, int v)

    {

        adj[u].push_back(v);

    }

    int main()

    {

        int n = 7;

        vector <int> adj[n];

        addEdge(adj, 0, 1);

        addEdge(adj, 0, 2);

        addEdge(adj, 1, 2);

        addEdge(adj, 1, 3);

        addEdge(adj, 2, 3);

        addEdge(adj, 3, 4);

        addEdge(adj, 3, 5);

        addEdge(adj, 4, 6);

        addEdge(adj, 5, 6);

        findShortestPaths(adj, 0, 7);

        return 0;

    }

    Java

    import java.io.*;

    import java.util.*;

    class GFG

    {

        static void BFS(Vector<Integer>[] adj, int src,

                      int dist[], int paths[], int n)

        {

            boolean[] visited = new boolean[n];

            for (int i = 0; i < n; i++)

                visited[i] = false;

            dist[src] = 0;

            paths[src] = 1;

            Queue<Integer> q = new LinkedList<>();

            q.add(src);

            visited[src] = true;

            while (!q.isEmpty())

            {

                int curr = q.peek();

                q.poll();

                for (int x : adj[curr])

                {

                    if (visited[x] == false)

                    {

                        q.add(x);

                        visited[x] = true;

                    }

                    if (dist[x] > dist[curr] + 1)

                    {

                        dist[x] = dist[curr] + 1;

                        paths[x] = paths[curr];

                    }

                    else if (dist[x] == dist[curr] + 1)

                        paths[x] += paths[curr];

                }

            }

        }

        static void findShortestPaths(Vector<Integer> adj[],

                                               int s, int n)

        {

            int[] dist = new int[n], paths = new int[n];

            for (int i = 0; i < n; i++)

                dist[i] = Integer.MAX_VALUE;

            for (int i = 0; i < n; i++)

                paths[i] = 0;

            BFS(adj, s, dist, paths, n);

            System.out.print("Numbers of shortest Paths are: ");

            for (int i = 0; i < n; i++)

                System.out.print(paths[i] + " ");

        }

        static void addEdge(Vector<Integer> adj[],

                                     int u, int v)

        {

            adj[u].add(v);

        }

        public static void main(String[] args)

        {

            int n = 7;

            Vector<Integer>[] adj = new Vector[n];

            for (int i = 0; i < n; i++)

                adj[i] = new Vector<>();

            addEdge(adj, 0, 1);

            addEdge(adj, 0, 2);

            addEdge(adj, 1, 2);

            addEdge(adj, 1, 3);

            addEdge(adj, 2, 3);

            addEdge(adj, 3, 4);

            addEdge(adj, 3, 5);

            addEdge(adj, 4, 6);

            addEdge(adj, 5, 6);

            findShortestPaths(adj, 0, 7);

        }

    }

    Python3

    from collections import deque

    from sys import maxsize as INT_MAX

    def BFS(adj: list, src: int, dist: list, paths: list, n: int):

        visited = [False] * n

        dist[src] = 0

        paths[src] = 1

        q = deque()

        q.append(src)

        visited[src] = True

        while q:

            curr = q[0]

            q.popleft()

            for x in adj[curr]:

                if not visited[x]:

                    q.append(x)

                    visited[x] = True

                if dist[x] > dist[curr] + 1:

                    dist[x] = dist[curr] + 1

                    paths[x] = paths[curr]

                elif dist[x] == dist[curr] + 1:

                    paths[x] += paths[curr]

    def findShortestPaths(adj: list, s: int, n: int):

        dist = [INT_MAX] * n

        paths = [0] * n

        BFS(adj, s, dist, paths, n)

        print("Numbers of shortest Paths are:", end=" ")

        for i in paths:

            print(i, end=" ")

    def addEdge(adj: list, u: int, v: int):

        adj[u].append(v)

    if __name__ == "__main__":

        n = 7

        adj = [0] * n

        for i in range(n):

            adj[i] = []

        addEdge(adj, 0, 1)

        addEdge(adj, 0, 2)

        addEdge(adj, 1, 2)

        addEdge(adj, 1, 3)

        addEdge(adj, 2, 3)

        addEdge(adj, 3, 4)

        addEdge(adj, 3, 5)

        addEdge(adj, 4, 6)

        addEdge(adj, 5, 6)

        findShortestPaths(adj, 0, 7)

    C#

    using System;

    using System.Collections.Generic;

    class GFG

    {

        static void BFS(List<int>[] adj, int src,

                    int []dist, int []paths, int n)

        {

            bool[] visited = new bool[n];

            for (int i = 0; i < n; i++)

                visited[i] = false;

            dist[src] = 0;

            paths[src] = 1;

            List<int> q = new List<int>();

            q.Add(src);

            visited[src] = true;

            while (q.Count != 0)

            {

                int curr = q[0];

                q.RemoveAt(0);

                foreach (int x in adj[curr])

                {

                    if (visited[x] == false)

                    {

                        q.Add(x);

                        visited[x] = true;

                    }

                    if (dist[x] > dist[curr] + 1)

                    {

                        dist[x] = dist[curr] + 1;

                        paths[x] = paths[curr];

                    }

                    else if (dist[x] == dist[curr] + 1)

                        paths[x] += paths[curr];

                }

            }

        }

        static void findShortestPaths(List<int> []adj,

                                            int s, int n)

        {

            int[] dist = new int[n], paths = new int[n];

            for (int i = 0; i < n; i++)

                dist[i] = int.MaxValue;

            for (int i = 0; i < n; i++)

                paths[i] = 0;

            BFS(adj, s, dist, paths, n);

            Console.Write("Numbers of shortest Paths are: ");

            for (int i = 0; i < n; i++)

                Console.Write(paths[i] + " ");

        }

        static void addEdge(List<int> []adj,

                                    int u, int v)

        {

            adj[u].Add(v);

        }

        public static void Main(String[] args)

        {

            int n = 7;

            List<int>[] adj = new List<int>[n];

            for (int i = 0; i < n; i++)

                adj[i] = new List<int>();

            addEdge(adj, 0, 1);

            addEdge(adj, 0, 2);

            addEdge(adj, 1, 2);

            addEdge(adj, 1, 3);

            addEdge(adj, 2, 3);

            addEdge(adj, 3, 4);

            addEdge(adj, 3, 5);

            addEdge(adj, 4, 6);

            addEdge(adj, 5, 6);

            findShortestPaths(adj, 0, 7);

        }

    }

    Javascript

    <script>

    function  BFS(adj,src,dist,paths,n)

    {

        let visited = new Array(n);

            for (let i = 0; i < n; i++)

                visited[i] = false;

            dist[src] = 0;

            paths[src] = 1;

            let q = [];

            q.push(src);

            visited[src] = true;

            while (q.length!=0)

            {

                let curr = q[0];

                q.shift();

                for (let x of adj[curr].values())

                {

                    if (visited[x] == false)

                    {

                        q.push(x);

                        visited[x] = true;

                    }

                    if (dist[x] > dist[curr] + 1)

                    {

                        dist[x] = dist[curr] + 1;

                        paths[x] = paths[curr];

                    }

                    else if (dist[x] == dist[curr] + 1)

                        paths[x] += paths[curr];

                }

            }

    }

    function findShortestPaths(adj,s,n)

    {

        let dist = new Array(n), paths = new Array(n);

            for (let i = 0; i < n; i++)

                dist[i] = Number.MAX_VALUE;

            for (let i = 0; i < n; i++)

                paths[i] = 0;

            BFS(adj, s, dist, paths, n);

            document.write("Numbers of shortest Paths are: ");

            for (let i = 0; i < n; i++)

                document.write(paths[i] + " ");

    }

    function addEdge(adj,u,v)

    {

        adj[u].push(v);

    }

    let n = 7;

    let adj = new Array(n);

    for (let i = 0; i < n; i++)

        adj[i] = [];

    addEdge(adj, 0, 1);

    addEdge(adj, 0, 2);

    addEdge(adj, 1, 2);

    addEdge(adj, 1, 3);

    addEdge(adj, 2, 3);

    addEdge(adj, 3, 4);

    addEdge(adj, 3, 5);

    addEdge(adj, 4, 6);

    addEdge(adj, 5, 6);

    findShortestPaths(adj, 0, 7);

    </script>

    Output

    Numbers of shortest Paths are: 1 1 1 2 2 2 4 

    Time Complexity : O(V + E)

    Last Updated :
    17 Aug, 2022

    Like Article

    Save Article

    Vote for difficulty

    Current difficulty :
    Hard

    Сайт переезжает. Большинство статей уже перенесено на новую версию.
    Скоро добавим автоматические переходы, но пока обновленную версию этой статьи можно найти там.

    Задача

    Дан ориентированный граф (G = (V, E)), а также вершина (s).
    Найти длину кратчайшего пути от (s) до каждой из вершин графа. Длина пути — количество рёбер в нём.

    BFS

    BFS — breadth-first search, или же поиск в ширину.

    Этот алгоритм позволяет решать следующую задачу.

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

    1. Создадим массив (dist) расстояний. Изначально (dist[s] = 0) (поскольку расстояний от вершины до самой себя равно (0)) и (dist[v] = infty) для (v neq s).
    2. Создадим очередь (q). Изначально в (q) добавим вершину (s).
    3. Пока очередь (q) непуста, делаем следующее:
      1. Извлекаем вершину (v) из очереди.
      2. Рассматриваем все рёбра ((v, u) in E). Для каждого такого ребра пытаемся сделать релаксацию: если (dist[v] + 1 < dist[u]), то мы делаем присвоение (dist[u] = dist[v] + 1) и добавляем вершину (u) в очередь.

    Визуализации:

    • https://visualgo.net/mn/dfsbfs

    • https://www.hackerearth.com/practice/algorithms/graphs/breadth-first-search/visualize/

    Интуитивное понимание алгоритма

    Можно представить, что мы поджигаем вершину (s). Каждый шаг алгоритма — это распространение огня на соседние вершины. Понятно, что огонь доберётся до вершины по кратчайшему пути.

    Заметьте, что этот алгоритм очень похож на DFS — достаточно заменить очередь на стек и поиск в ширину станет поиском в глубину. Действительно, оба алгоритма при обработке вершины просто записывают всех непосещенных соседей, в которые из неё есть ребро, в структуру данных, и после этого выбирает следующую вершину для обработки в структуре данных. В DFS это стек (благодаря рекурсии), поэтому мы сначала записываем соседа, идем в обрабатываем его полностью, а потом начинаем обрабатывать следующего соседа. В BFS это очередь, поэтому мы кидаем сразу всех соседей, а потом начинаем обрабатывать вообще другую вершину — ту непосещенную, которую мы положили в очередь раньше всего.

    Оба алгоритма позволяют обойти граф целиком — посетить каждую вершину ровно один раз. Поэтому они оба подходят для таких задач как: * поиск компонент связности * проверка графа на двудольность * построение остова

    Реализация на C++

    n — количество вершин в графе; adj — список смежности

    vector<int> bfs(int s) {
        // длина любого кратчайшего пути не превосходит n - 1,
        // поэтому n - достаточное значение для "бесконечности";
        // после работы алгоритма dist[v] = n, если v недостижима из s
        vector<int> dist(n, n);
        dist[s] = 0;
        queue<int> q;
        q.push(s);
    
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int u : adj[v]) {
                if (dist[u] > dist[v] + 1) {
                    dist[u] = dist[v] + 1;
                    q.push(u);
                }
            }
        }
    
        return dist;
    }

    Свойства кратчайших путей

    Обозначение: (d(v)) — длина кратчайшего пути от (s) до (v).

    Лемма 1. > Пусть ((u, v) in E), тогда (d(v) leq d(u) + 1).

    Действительно, существует путь из (s) в (u) длины (d(u)), а также есть ребро ((u, v)), следовательно, существует путь из (s) в (v) длины (d(u) + 1). А значит кратчайший путь из (s) в (v) имеет длину не более (d(u) + 1),

    Лемма 2. > Рассмотрим кратчайший путь от (s) до (v). Обозначим его как (u_1, u_2, dots u_k) ((u_1 = s) и (u_k = v), а также (k = d(v) + 1)).
    > Тогда (forall (i < k): d(u_i) + 1 = d(u_{i + 1})).

    Действительно, пусть для какого-то (i < k) это не так. Тогда, используя лемму 1, имеем: (d(u_i) + 1 > d(u_{i + 1})). Тогда мы можем заменить первые (i + 1) вершин пути на вершины из кратчайшего пути из (s) в (u_{i + 1}). Полученный путь стал короче, но мы рассматривали кратчайший путь — противоречие.

    Корректность

    Утверждение. > 1. Расстояния до тех вершин, которые были добавлены в очередь, посчитаны корректно. > 2. Вершины лежат в очереди в порядке неубывания расстояния, притом разность между кратчайшими расстояними до вершин в очереди не превосходит (1).

    Докажем это по индукции по количеству итераций алгоритма (итерация — извлечение вершины из очереди и дальнейшая релаксация).

    База очевидна.
    Переход. Сначала докажем первую часть. Предположим, что (dist[v] + 1 < dist[u]), но (dist[v] + 1) — некорректное расстояние до вершины (u), то есть (dist[v] + 1 neq d(u)). Тогда по лемме 1: (d(u) < dist[v] + 1). Рассмотрим предпоследнюю вершину (w) на кратчайшем пути от (s) до (u). Тогда по лемме 2: (d(w) + 1 = d(u)). Следовательно, (d(w) + 1 < dist[v] + 1) и (d(w) < dist[v]). Но тогда по предположению индукции (w) была извлечена раньше (v), следовательно, при релаксации из неё в очередь должна была быть добавлена вершина (u) с уже корректным расстоянием. Противоречие.
    Теперь докажем вторую часть. По предположению индукции в очереди лежали некоторые вершины (u_1, u_2, dots u_k), для которых выполнялось следующее: (dist[u_1] leq dist[u_2] leq dots leq dist[u_k]) и (dist[u_k] — dist[u_1] leq 1). Мы извлекли вершину (v = u_1) и могли добавить в конец очереди какие-то вершины с расстоянием (dist[v] + 1). Если (k = 1), то утверждение очевидно. В противном случае имеем (dist[u_k] — dist[u_1] leq 1 leftrightarrow dist[u_k] — dist[v] leq 1 leftrightarrow dist[u_k] leq dist[v] + 1), то есть упорядоченность сохранилась. Осталось показать, что ((dist[v] + 1) — dist[u_2] leq 1), но это равносильно (dist[v] leq dist[u_2]), что, как мы знаем, верно.

    Время работы

    Из доказанного следует, что каждая достижимая из (s) вершина будет добавлена в очередь ровно (1) раз, недостижимые вершины добавлены не будут. Каждое ребро, соединяющее достижимые вершины, будет рассмотрено ровно (2) раза. Таким образом, алгоритм работает за (O(V+ E)) времени, при условии, что граф хранится в виде списка смежности.

    Неориентированные графы

    Если дан неориентированный граф, его можно рассматривать как ориентированный граф с двумя обратными друг другу ориентированными рёбрами.

    Восстановление пути

    Пусть теперь заданы 2 вершины (s) и (t), и необходимо не только найти длину кратчайшего пути из (s) в (t), но и восстановить какой-нибудь из кратчайших путей между ними. Всё ещё можно воспользоваться алгоритмом BFS, но необходимо ещё и поддерживать массив предков (p), в котором для каждой вершины будет храниться предыдущая вершина на кратчайшем пути.

    Поддерживать этот массив просто: при релаксации нужно просто запоминать, из какой вершины мы прорелаксировали в данную. Также будем считать, что (p[s] = -1): у стартовой вершины предок — некоторая несуществующая вершина.

    Восстановление пути делается с конца. Мы знаем последнюю вершину пути — это (t). Далее, мы сводим задачу к меньшей, переходя к нахождению пути из (s) в (p[t]).

    Реализация BFS с восстановлением пути

    // теперь bfs принимает 2 вершины, между которыми ищется пути
    // bfs возвращает кратчайший путь из s в t, или же пустой vector, если пути нет
    vector<int> bfs(int s, int t) {
        vector<int> dist(n, n);
        vector<int> p(n, -1);
        dist[s] = 0;
        queue<int> q;
        q.push(s);
    
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int u : adj[v]) {
                if (dist[u] > dist[v] + 1) {
                    p[u] = v;
                    dist[u] = dist[v] + 1;
                    q.push(u);
                }
            }
        }
        
        // если пути не существует, возвращаем пустой vector
        if (dist[t] == n) {
            return {};
        }
    
        vector<int> path;
        while (t != -1) {
            path.push_back(t);
            t = p[t];
        }
        
        // путь был рассмотрен в обратном порядке, поэтому его нужно перевернуть
        reverse(path.begin(), path.end());
        return path;
    }

    Проверка принадлежности вершины кратчайшему пути

    Дан ориентированный граф (G), найти все вершины, которые принадлежат хотя бы одному кратчайшему пути из (s) в (t).

    Запустим из вершины (s) в графе (G) BFS — найдём расстояния (d_1). Построим транспонированный граф (G^T) — граф, в котором каждое ребро заменено на противоположное. Запустим из вершины (t) в графе (G^T) BFS — найдём расстояния (d_2).

    Теперь очевидно, что (v) принадлежит хотя бы одному кратчайшему пути из (s) в (t) тогда и только тогда, когда (d_1(v) + d_2(v) = d_1(t)) — это значит, что есть путь из (s) в (v) длины (d_1(v)), а затем есть путь из (v) в (t) длины (d_2(v)), и их суммарная длина совпадает с длиной кратчайшего пути из (s) в (t).

    Кратчайший цикл в ориентированном графе

    Найти цикл минимальной длины в ориентированном графе.

    Попытаемся из каждой вершины найти кратчайший цикл, проходящий через неё, с помощью BFS. Это делается аналогично обычному BFS: мы должны найти расстояний от вершины до самой себя, при этом не считая, что оно равно (0).

    Итого, у нас (|V|) запусков BFS, и каждый запуск работает за (O(|V| + |E|)). Тогда общее время работы составляет (O(|V|^2 + |V| |E|)). Если инициализировать массив (dist) единожды, а после каждого запуска BFS возвращать исходные значения только для достижимых вершин, решение будет работать за (O(|V||E|)).

    Задача

    Дан взвешенный ориентированный граф (G = (V, E)), а также вершина (s). Длина ребра ((u, v)) равна (w(u, v)). Длины всех рёбер неотрицательные.
    Найти длину кратчайшего пути от (s) до каждой из вершин графа. Длина пути — сумма длин рёбер в нём.

    Алгоритм Дейкстры

    Алгоритм Дейкстры решает приведённую выше задачу. Он работает следующим образом.

    1. Создать массив (dist) расстояний. Изначально (dist[s] = 0) и (dist[v] = infty) для (v neq s).
    2. Создать булёв массив (used), (used[v] = 0) для всех вершин (v) — в нём мы будем отмечать, совершалась ли релаксация из вершины.
    3. Пока существует вершина (v) такая, что (used[v] = 0) и (dist[v] neq infty), притом, если таких вершин несколько, то (v) — вершина с минимальным (dist[v]), делать следующее:
      1. Пометить, что мы совершали релаксацию из вершины (v), то есть присвоить (used[v] = 1).
      2. Рассматриваем все рёбра ((v, u) in E). Для каждого ребра пытаемся сделать релаксацию: если (dist[v] + w(v, u) < dist[u]), присвоить (dist[u] = dist[v] + w(v, u)).

    Иными словами, алгоритм на каждом шаге находит вершину, до которой расстояние сейчас минимально и из которой ещё не была произведена релаксация, и делает её.

    Посчитаем, за сколько работает алгоритм. Мы (V) раз ищем вершину минимальным (dist), поиск минимума у нас линейный за (O(V)), отсюда (O(V^2)). Обработка ребер у нас происходит суммарно за (O(E)), потому что на каждое ребро мы тратим (O(1)) действий. Так мы находим финальную асимптотику: (O(V^2 + E)).

    Реализация на C++

    Рёбра будем хранить как pair<int, int>, где первое число пары — куда оно ведёт; а второе — длина ребра.

    // INF - infinity - бесконечность
    const long long INF = (long long) 1e18 + 1;
    
    vector<long long> dijkstra(int s) {
        vector<long long> dist(n, INF);
        dist[s] = 0;
        vector<bool> used(n);
        
        while (true) {
            // находим вершину, из которой будем релаксировать
            int v = -1;
            for (int i = 0; i < n; i++) {
                if (!used[i] && (v == -1 || dist[i] < dist[v])) {
                    v = i;
                }
            }
            
            // если не нашли подходящую вершину, прекращаем работу алгоритма
            if (v == -1) {
                break;
            }
            
            for (auto &e : adj[v]) {
                int u = e.first;
                int len = e.second;
                if (dist[u] > dist[v] + len) {
                    dist[u] = dist[v] + len;
                }
            }
        }
        
        return dist;
    }

    Восстановление пути

    Восстановление пути в алгоритме Дейкстры делается аналогично восстановлению пути в BFS (и любой динамике).

    Дейкстра на сете

    Искать вершину с минимальным (dist) можно гораздо быстрее, используя такую структуру данных как очередь с приоритетом. Нам нужно хранить пары ((dist, index)) и уметь делать такие операции: * Извлечь минимум (чтобы обработать новую вершину) * Удалить вершину по индексу (чтобы уменьшить (dist) до какого-то соседа) * Добавить новую вершину (чтобы уменьшить (dist) до какого-то соседа)

    Для этого используют, например, кучу или сет. Удобно помимо сета хранить сам массив dist, который его дублирует, но хранит элементы по порядку. Тогда, чтобы заменить значение ((dist_1, u)) на ((dist_2, u)), нужно удалить из сета значение ((dist[u], u)), сделать (dist[u] = dist_2;) и добавить в сет ((dist[u], u)).

    Данный алгоритм будет работать за (V O(log V)) извлечений минимума и (O(E log V)) операций уменьшения расстояния до вершины (может быть сделано после каждого ребра). Поэтому алгоритм работает за (O(E log V)).

    Заметьте, что этот алгоритм не лучше и не хуже, чем без сета, который работает за (O(V^2 + E)). Ведь если (E = O(V^2)) (граф почти полный), то Дейкстра без сета работает быстрее, а если, наример, (E = O(V)), то Дейкстра на сете работает быстрее. Учитывайте это, когда выбираете алгоритм.

    Графы. Нахождение кратчайшего расстояния между двумя вершинами с помощью алгоритма Дейкстры

    Подробности
    Категория: Сортировка и поиск

    Алгоритм Дейкстры (англ. Dijkstra’s algorithm) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса.

    Dijkstra Animation.gif

    Лекция 2: Алгоритмы и методы сортировки. Алгоритмы нахождения кратчайшего пути в графе

    Лекция 5: Поиск в графах и обход. Алгоритм Дейкстры

    Алгоритмы и структуры данных, лекция 13

    Рассмотрим выполнение алгоритма на примере графа, показанного на рисунке.

    Пусть требуется найти кратчайшие расстояния от 1-й вершины до всех остальных.

    Dijkstra graph0.PNG

    Кружками обозначены вершины, линиями — пути между ними (рёбра графа). В кружках обозначены номера вершин, над рёбрами обозначена их «цена» — длина пути. Рядом с каждой вершиной красным обозначена метка — длина кратчайшего пути в эту вершину из вершины 1.

    Dijkstra graph1.PNG

    Первый шаг. Рассмотрим шаг алгоритма Дейкстры для нашего примера. Минимальную метку имеет вершина 1. Её соседями являются вершины 2, 3 и 6.

    Dijkstra graph2.PNG

    Первый по очереди сосед вершины 1 — вершина 2, потому что длина пути до неё минимальна. Длина пути в неё через вершину 1 равна сумме значения метки вершины 1 и длины ребра, идущего из 1-й в 2-ю, то есть 0 + 7 = 7. Это меньше текущей метки вершины 2, бесконечности, поэтому новая метка 2-й вершины равна 7.

    Dijkstra graph3.PNG

    Аналогичную операцию проделываем с двумя другими соседями 1-й вершины — 3-й и 6-й.

    Dijkstra graph5.PNG

    Все соседи вершины 1 проверены. Текущее минимальное расстояние до вершины 1 считается окончательным и пересмотру не подлежит (то, что это действительно так, впервые доказал Э. Дейкстра). Вычеркнем её из графа, чтобы отметить, что эта вершина посещена.

    Dijkstra graph6.PNG

    Второй шаг. Шаг алгоритма повторяется. Снова находим «ближайшую» из непосещённых вершин. Это вершина 2 с меткой 7.

    Dijkstra graph7.PNG

    Снова пытаемся уменьшить метки соседей выбранной вершины, пытаясь пройти в них через 2-ю вершину. Соседями вершины 2 являются вершины 1, 3 и 4.

    Первый (по порядку) сосед вершины 2 — вершина 1. Но она уже посещена, поэтому с 1-й вершиной ничего не делаем.

    Следующий сосед вершины 2 — вершина 3, так как имеет минимальную метку из вершин, отмеченных как не посещённые. Если идти в неё через 2, то длина такого пути будет равна 17 (7 + 10 = 17). Но текущая метка третьей вершины равна 9, а это меньше 17, поэтому метка не меняется.

    Dijkstra graph9.PNG

    Ещё один сосед вершины 2 — вершина 4. Если идти в неё через 2-ю, то длина такого пути будет равна сумме кратчайшего расстояния до 2-й вершины и расстояния между вершинами 2 и 4, то есть 22 (7 + 15 = 22). Поскольку 22<infty, устанавливаем метку вершины 4 равной 22.

    Dijkstra graph8.PNG

    Все соседи вершины 2 просмотрены, замораживаем расстояние до неё и помечаем её как посещённую.

    Dijkstra graph10.PNG

    Третий шаг. Повторяем шаг алгоритма, выбрав вершину 3. После её «обработки» получим такие результаты:

    Dijkstra graph11.PNG

    Дальнейшие шаги. Повторяем шаг алгоритма для оставшихся вершин. Это будут вершины 6, 4 и 5, соответственно порядку.

    Dijkstra graph12.PNG Dijkstra graph13.PNG Dijkstra graph14.PNG

    Завершение выполнения алгоритма. Алгоритм заканчивает работу, когда нельзя больше обработать ни одной вершины. В данном примере все вершины зачёркнуты, однако ошибочно полагать, что так будет в любом примере — некоторые вершины могут остаться незачёркнутыми, если до них нельзя добраться, т. е. если граф несвязный. Результат работы алгоритма виден на последнем рисунке: кратчайший путь от вершины 1 до 2-й составляет 7, до 3-й — 9, до 4-й — 20, до 5-й — 20, до 6-й — 11.

    Реализация алгоритма на различных языках программирования :

    C++

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    const int V=6;
    //алгоритм Дейкстры
    void Dijkstra(int GR[V][V], int st)
    {
    int distance[V], count, index, i, u, m=st+1;
    bool visited[V];
    for (i=0; i<V; i++)
    {
    distance[i]=INT_MAX; visited[i]=false;
    }
    distance[st]=0;
    for (count=0; count<V-1; count++)
    {
    int min=INT_MAX;
    for (i=0; i<V; i++)
    if (!visited[i] && distance[i]<=min)
    {
    min=distance[i]; index=i;
    }
    u=index;
    visited[u]=true;
    for (i=0; i<V; i++)
    if (!visited[i] && GR[u][i] && distance[u]!=INT_MAX &&
    distance[u]+GR[u][i]<distance[i])
    distance[i]=distance[u]+GR[u][i];
    }
    cout<<"Стоимость пути из начальной вершины до остальных:tn";
    for (i=0; i<V; i++) if (distance[i]!=INT_MAX)
    cout<<m<<" > "<<i+1<<" = "<<distance[i]<<endl;
    else cout<<m<<" > "<<i+1<<" = "<<"маршрут недоступен"<<endl;
    }
    //главная функция
    void main()
    {
    setlocale(LC_ALL, "Rus");
    int start, GR[V][V]={
    {0, 1, 4, 0, 2, 0},
    {0, 0, 0, 9, 0, 0},
    {4, 0, 0, 7, 0, 0},
    {0, 9, 7, 0, 0, 2},
    {0, 0, 0, 0, 0, 8},
    {0, 0, 0, 0, 0, 0}};
    cout<<"Начальная вершина >> "; cin>>start;
    Dijkstra(GR, start-1);
    system("pause>>void");
    }

     kvodo

    Pascal

    program DijkstraAlgorithm;
    uses crt;
    const V=6; inf=100000;
    type vektor=array[1..V] of integer;
    var start: integer;
    const GR: array[1..V, 1..V] of integer=(
    (0, 1, 4, 0, 2, 0),
    (0, 0, 0, 9, 0, 0),
    (4, 0, 0, 7, 0, 0),
    (0, 9, 7, 0, 0, 2),
    (0, 0, 0, 0, 0, 8),
    (0, 0, 0, 0, 0, 0));
    {алгоритм Дейкстры}
    procedure Dijkstra(GR: array[1..V, 1..V] of integer; st: integer);
    var count, index, i, u, m, min: integer;
    distance: vektor;
    visited: array[1..V] of boolean;
    begin
    m:=st;
    for i:=1 to V do
    begin
    distance[i]:=inf; visited[i]:=false;
    end;
    distance[st]:=0;
    for count:=1 to V-1 do
    begin
    min:=inf;
    for i:=1 to V do
    if (not visited[i]) and (distance[i]<=min) then
    begin
    min:=distance[i]; index:=i;
    end;
    u:=index;
    visited[u]:=true;
    for i:=1 to V do
    if (not visited[i]) and (GR[u, i]<>0) and (distance[u]<>inf) and
    (distance[u]+GR[u, i]<distance[i]) then
    distance[i]:=distance[u]+GR[u, i];
    end;
    write('Стоимость пути из начальной вершины до остальных:'); writeln;
    for i:=1 to V do
    if distance[i]<>inf then
    writeln(m,' > ', i,' = ', distance[i])
    else writeln(m,' > ', i,' = ', 'маршрут недоступен');
    end;
    {основной блок программы}
    begin
    clrscr;
    write('Начальная вершина >> '); read(start);
    Dijkstra(GR, start);
    end.

    kvodo

    Java

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.StringTokenizer;
    
    public class Solution {
       
        private static int INF = Integer.MAX_VALUE / 2;
       
        private int n; //количество вершин в орграфе
        private int m; //количествое дуг в орграфе
        private ArrayList<Integer> adj[]; //список смежности
        private ArrayList<Integer> weight[]; //вес ребра в орграфе
        private boolean used[]; //массив для хранения информации о пройденных и не пройденных вершинах
        private int dist[]; //массив для хранения расстояния от стартовой вершины
        //массив предков, необходимых для восстановления кратчайшего пути из стартовой вершины
        private int pred[];
        int start; //стартовая вершина, от которой ищется расстояние до всех других
       
        private BufferedReader cin;
        private PrintWriter cout;
        private StringTokenizer tokenizer;
       
        //процедура запуска алгоритма Дейкстры из стартовой вершины
        private void dejkstra(int s) {
            dist[s] = 0; //кратчайшее расстояние до стартовой вершины равно 0
            for (int iter = 0; iter < n; ++iter) {
                int v = -1;
                int distV = INF;
                //выбираем вершину, кратчайшее расстояние до которого еще не найдено
                for (int i = 0; i < n; ++i) {
                    if (used[i]) {
                        continue;
                    }
                    if (distV < dist[i]) {
                        continue;
                    }
                    v = i;
                    distV = dist[i];
                }
                //рассматриваем все дуги, исходящие из найденной вершины
                for (int i = 0; i < adj[v].size(); ++i) {
                    int u = adj[v].get(i);
                    int weightU = weight[v].get(i);
                    //релаксация вершины
                    if (dist[v] + weightU < dist[u]) {
                        dist[u] = dist[v] + weightU;
                        pred[u] = v;
                    }
                }
                //помечаем вершину v просмотренной, до нее найдено кратчайшее расстояние
                used[v] = true;
            }
        }
       
        //процедура считывания входных данных с консоли
        private void readData() throws IOException {
            cin = new BufferedReader(new InputStreamReader(System.in));
            cout = new PrintWriter(System.out);
            tokenizer = new StringTokenizer(cin.readLine());
           
            n = Integer.parseInt(tokenizer.nextToken()); //считываем количество вершин графа
            m = Integer.parseInt(tokenizer.nextToken()); //считываем количество ребер графа
            start = Integer.parseInt(tokenizer.nextToken()) - 1;
           
            //инициализируем списка смежности графа размерности n
            adj = new ArrayList[n];
            for (int i = 0; i < n; ++i) {
                adj[i] = new ArrayList<Integer>();
            }
            //инициализация списка, в котором хранятся веса ребер
            weight = new ArrayList[n];
            for (int i = 0; i < n; ++i) {
                weight[i] = new ArrayList<Integer>();
            }
           
            //считываем граф, заданный списком ребер
            for (int i = 0; i < m; ++i) {
                tokenizer = new StringTokenizer(cin.readLine());
                int u = Integer.parseInt(tokenizer.nextToken());
                int v = Integer.parseInt(tokenizer.nextToken());
                int w = Integer.parseInt(tokenizer.nextToken());
                u--;
                v--;
                adj[u].add(v);
                weight[u].add(w);
            }
           
            used = new boolean[n];
            Arrays.fill(used, false);
           
            pred = new int[n];
            Arrays.fill(pred, -1);
           
            dist = new int[n];
            Arrays.fill(dist, INF);
           
        }
    
        //процедура восстановления кратчайшего пути по массиву предком
        void printWay(int v) {
            if (v == -1) {
                return;
            }
            printWay(pred[v]);
            cout.print((v + 1) + " ");
        }
       
        //процедура вывода данных в консоль
        private void printData() throws IOException {
            for (int v = 0; v < n; ++v) {
                if (dist[v] != INF) {
                    cout.print(dist[v] + " ");
                } else {
                    cout.print("-1 ");
                }
            }
            cout.println();
            for (int v = 0; v < n; ++v) {
                cout.print((v + 1) + ": ");
                if (dist[v] != INF) {
                    printWay(v);
                }
                cout.println();
            }
                   
            cin.close();
            cout.close();
        }
       
        private void run() throws IOException {
            readData();
            dejkstra(start);
            printData();
           
            cin.close();
            cout.close();
        }
       
        public static void main(String[] args) throws IOException {
            Solution solution = new Solution();
            solution.run();
        }
    }

    cybern.ru

    Ещё один вариант:

     
    import java.io.*;
    import java.util.*;
     
    public class Dijkstra {
       private static final Graph.Edge[] GRAPH = {
          new Graph.Edge("a", "b", 7),
          new Graph.Edge("a", "c", 9),
          new Graph.Edge("a", "f", 14),
          new Graph.Edge("b", "c", 10),
          new Graph.Edge("b", "d", 15),
          new Graph.Edge("c", "d", 11),
          new Graph.Edge("c", "f", 2),
          new Graph.Edge("d", "e", 6),
          new Graph.Edge("e", "f", 9),
       };
       private static final String START = "a";
       private static final String END = "e";
     
       public static void main(String[] args) {
          Graph g = new Graph(GRAPH);
          g.dijkstra(START);
          g.printPath(END);
          //g.printAllPaths();
       }
    }
     
    class Graph {
       private final Map<String, Vertex> graph; // mapping of vertex names to Vertex objects, built from a set of Edges
     
       /** One edge of the graph (only used by Graph constructor) */
       public static class Edge {
          public final String v1, v2;
          public final int dist;
          public Edge(String v1, String v2, int dist) {
             this.v1 = v1;
             this.v2 = v2;
             this.dist = dist;
          }
       }
     
       /** One vertex of the graph, complete with mappings to neighbouring vertices */
       public static class Vertex implements Comparable<Vertex> {
          public final String name;
          public int dist = Integer.MAX_VALUE; // MAX_VALUE assumed to be infinity
          public Vertex previous = null;
          public final Map<Vertex, Integer> neighbours = new HashMap<>();
     
          public Vertex(String name) {
             this.name = name;
          }
     
          private void printPath() {
             if (this == this.previous) {
                System.out.printf("%s", this.name);
             } else if (this.previous == null) {
                System.out.printf("%s(unreached)", this.name);
             } else {
                this.previous.printPath();
                System.out.printf(" -> %s(%d)", this.name, this.dist);
             }
          }
     
          public int compareTo(Vertex other) {
             return Integer.compare(dist, other.dist);
          }
       }
     
       /** Builds a graph from a set of edges */
       public Graph(Edge[] edges) {
          graph = new HashMap<>(edges.length);
     
          //one pass to find all vertices
          for (Edge e : edges) {
             if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex(e.v1));
             if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex(e.v2));
          }
     
          //another pass to set neighbouring vertices
          for (Edge e : edges) {
             graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
             //graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph
          }
       }
     
       /** Runs dijkstra using a specified source vertex */ 
       public void dijkstra(String startName) {
          if (!graph.containsKey(startName)) {
             System.err.printf("Graph doesn't contain start vertex "%s"n", startName);
             return;
          }
          final Vertex source = graph.get(startName);
          NavigableSet<Vertex> q = new TreeSet<>();
     
          // set-up vertices
          for (Vertex v : graph.values()) {
             v.previous = v == source ? source : null;
             v.dist = v == source ? 0 : Integer.MAX_VALUE;
             q.add(v);
          }
     
          dijkstra(q);
       }
     
       /** Implementation of dijkstra's algorithm using a binary heap. */
       private void dijkstra(final NavigableSet<Vertex> q) {      
          Vertex u, v;
          while (!q.isEmpty()) {
     
             u = q.pollFirst(); // vertex with shortest distance (first iteration will return source)
             if (u.dist == Integer.MAX_VALUE) break; // we can ignore u (and any other remaining vertices) since they are unreachable
     
             //look at distances to each neighbour
             for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) {
                v = a.getKey(); //the neighbour in this iteration
     
                final int alternateDist = u.dist + a.getValue();
                if (alternateDist < v.dist) { // shorter path to neighbour found
                   q.remove(v);
                   v.dist = alternateDist;
                   v.previous = u;
                   q.add(v);
                } 
             }
          }
       }
     
       /** Prints a path from the source to the specified vertex */
       public void printPath(String endName) {
          if (!graph.containsKey(endName)) {
             System.err.printf("Graph doesn't contain end vertex "%s"n", endName);
             return;
          }
     
          graph.get(endName).printPath();
          System.out.println();
       }
       /** Prints the path from the source to every vertex (output order is not guaranteed) */
       public void printAllPaths() {
          for (Vertex v : graph.values()) {
             v.printPath();
             System.out.println();
          }
       }
    }

    rosettacode.org

    C

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    //#define BIG_EXAMPLE
     
    typedef struct node_t node_t, *heap_t;
    typedef struct edge_t edge_t;
    struct edge_t {
    	node_t *nd;	/* target of this edge */
    	edge_t *sibling;/* for singly linked list */
    	int len;	/* edge cost */
    };
    struct node_t {
    	edge_t *edge;	/* singly linked list of edges */
    	node_t *via;	/* where previous node is in shortest path */
    	double dist;	/* distance from origining node */
    	char name[8];	/* the, er, name */
    	int heap_idx;	/* link to heap position for updating distance */
    };
     
     
    /* --- edge management --- */
    #ifdef BIG_EXAMPLE
    #	define BLOCK_SIZE (1024 * 32 - 1)
    #else
    #	define BLOCK_SIZE 15
    #endif
    edge_t *edge_root = 0, *e_next = 0;
     
    /* Don't mind the memory management stuff, they are besides the point.
       Pretend e_next = malloc(sizeof(edge_t)) */
    void add_edge(node_t *a, node_t *b, double d)
    {
    	if (e_next == edge_root) {
    		edge_root = malloc(sizeof(edge_t) * (BLOCK_SIZE + 1));
    		edge_root[BLOCK_SIZE].sibling = e_next;
    		e_next = edge_root + BLOCK_SIZE;
    	}
    	--e_next;
     
    	e_next->nd = b;
    	e_next->len = d;
    	e_next->sibling = a->edge;
    	a->edge = e_next;
    }
     
    void free_edges()
    {
    	for (; edge_root; edge_root = e_next) {
    		e_next = edge_root[BLOCK_SIZE].sibling;
    		free(edge_root);
    	}
    }
     
    /* --- priority queue stuff --- */
    heap_t *heap;
    int heap_len;
     
    void set_dist(node_t *nd, node_t *via, double d)
    {
    	int i, j;
     
    	/* already knew better path */
    	if (nd->via && d >= nd->dist) return;
     
    	/* find existing heap entry, or create a new one */
    	nd->dist = d;
    	nd->via = via;
     
    	i = nd->heap_idx;
    	if (!i) i = ++heap_len;
     
    	/* upheap */
    	for (; i > 1 && nd->dist < heap[j = i/2]->dist; i = j)
    		(heap[i] = heap[j])->heap_idx = i;
     
    	heap[i] = nd;
    	nd->heap_idx = i;
    }
     
    node_t * pop_queue()
    {
    	node_t *nd, *tmp;
    	int i, j;
     
    	if (!heap_len) return 0;
     
    	/* remove leading element, pull tail element there and downheap */
    	nd = heap[1];
    	tmp = heap[heap_len--];
     
    	for (i = 1; i < heap_len && (j = i * 2) <= heap_len; i = j) {
    		if (j < heap_len && heap[j]->dist > heap[j+1]->dist) j++;
     
    		if (heap[j]->dist >= tmp->dist) break;
    		(heap[i] = heap[j])->heap_idx = i;
    	}
     
    	heap[i] = tmp;
    	tmp->heap_idx = i;
     
    	return nd;
    }
     
    /* --- Dijkstra stuff; unreachable nodes will never make into the queue --- */
    void calc_all(node_t *start)
    {
    	node_t *lead;
    	edge_t *e;
     
    	set_dist(start, start, 0);
    	while ((lead = pop_queue()))
    		for (e = lead->edge; e; e = e->sibling)
    			set_dist(e->nd, lead, lead->dist + e->len);
    }
     
    void show_path(node_t *nd)
    {
    	if (nd->via == nd)
    		printf("%s", nd->name);
    	else if (!nd->via)
    		printf("%s(unreached)", nd->name);
    	else {
    		show_path(nd->via);
    		printf("-> %s(%g) ", nd->name, nd->dist);
    	}
    }
     
    int main(void)
    {
    #ifndef BIG_EXAMPLE
    	int i;
     
    #	define N_NODES ('f' - 'a' + 1)
    	node_t *nodes = calloc(sizeof(node_t), N_NODES);
     
    	for (i = 0; i < N_NODES; i++)
    		sprintf(nodes[i].name, "%c", 'a' + i);
     
    #	define E(a, b, c) add_edge(nodes + (a - 'a'), nodes + (b - 'a'), c)
    	E('a', 'b', 7);	E('a', 'c', 9); E('a', 'f', 14);
    	E('b', 'c', 10);E('b', 'd', 15);E('c', 'd', 11);
    	E('c', 'f', 2); E('d', 'e', 6);	E('e', 'f', 9);
    #	undef E
     
    #else /* BIG_EXAMPLE */
    	int i, j, c;
     
    #	define N_NODES 4000
    	node_t *nodes = calloc(sizeof(node_t), N_NODES);
     
    	for (i = 0; i < N_NODES; i++)
    		sprintf(nodes[i].name, "%d", i + 1);
     
    	/* given any pair of nodes, there's about 50% chance they are not
    	   connected; if connected, the cost is randomly chosen between 0
    	   and 49 (inclusive! see output for consequences) */
    	for (i = 0; i < N_NODES; i++) {
    		for (j = 0; j < N_NODES; j++) {
    			/* majority of runtime is actually spent here */
    			if (i == j) continue;
    			c = rand() % 100;
    			if (c < 50) continue;
    			add_edge(nodes + i, nodes + j, c - 50);
    		}
    	}
     
    #endif
    	heap = calloc(sizeof(heap_t), N_NODES + 1);
    	heap_len = 0;
     
    	calc_all(nodes);
    	for (i = 0; i < N_NODES; i++) {
    		show_path(nodes + i);
    		putchar('n');
    	}
     
    #if 0
    	/* real programmers don't free memories (they use Fortran) */
    	free_edges();
    	free(heap);
    	free(nodes);
    #endif
    	return 0;
    }

    rosettacode.org

    PHP

    <?php
    function dijkstra($graph_array, $source, $target) {
        $vertices = array();
        $neighbours = array();
        foreach ($graph_array as $edge) {
            array_push($vertices, $edge[0], $edge[1]);
            $neighbours[$edge[0]][] = array("end" => $edge[1], "cost" => $edge[2]);
            $neighbours[$edge[1]][] = array("end" => $edge[0], "cost" => $edge[2]);
        }
        $vertices = array_unique($vertices);
     
        foreach ($vertices as $vertex) {
            $dist[$vertex] = INF;
            $previous[$vertex] = NULL;
        }
     
        $dist[$source] = 0;
        $Q = $vertices;
        while (count($Q) > 0) {
     
            // TODO - Find faster way to get minimum
            $min = INF;
            foreach ($Q as $vertex){
                if ($dist[$vertex] < $min) {
                    $min = $dist[$vertex];
                    $u = $vertex;
                }
            }
     
            $Q = array_diff($Q, array($u));
            if ($dist[$u] == INF or $u == $target) {
                break;
            }
     
            if (isset($neighbours[$u])) {
                foreach ($neighbours[$u] as $arr) {
                    $alt = $dist[$u] + $arr["cost"];
                    if ($alt < $dist[$arr["end"]]) {
                        $dist[$arr["end"]] = $alt;
                        $previous[$arr["end"]] = $u;
                    }
                }
            }
        }
        $path = array();
        $u = $target;
        while (isset($previous[$u])) {
            array_unshift($path, $u);
            $u = $previous[$u];
        }
        array_unshift($path, $u);
        return $path;
    }
     
    $graph_array = array(
                        array("a", "b", 7),
                        array("a", "c", 9),
                        array("a", "f", 14),
                        array("b", "c", 10),
                        array("b", "d", 15),
                        array("c", "d", 11),
                        array("c", "f", 2),
                        array("d", "e", 6),
                        array("e", "f", 9)
                   );
     
    $path = dijkstra($graph_array, "a", "e");
     
    echo "path is: ".implode(", ", $path)."n";
      

    Output is:

    path is: a, c, f, e

    rosettacode.org

    Python

    from collections import namedtuple, queue
    from pprint import pprint as pp
     
     
    inf = float('inf')
    Edge = namedtuple('Edge', 'start, end, cost')
     
    class Graph():
        def __init__(self, edges):
            self.edges = edges2 = [Edge(*edge) for edge in edges]
            self.vertices = set(sum(([e.start, e.end] for e in edges2), []))
     
        def dijkstra(self, source, dest):
            assert source in self.vertices
            dist = {vertex: inf for vertex in self.vertices}
            previous = {vertex: None for vertex in self.vertices}
            dist[source] = 0
            q = self.vertices.copy()
            neighbours = {vertex: set() for vertex in self.vertices}
            for start, end, cost in self.edges:
                neighbours[start].add((end, cost))
            #pp(neighbours)
     
            while q:
                u = min(q, key=lambda vertex: dist[vertex])
                q.remove(u)
                if dist[u] == inf or u == dest:
                    break
                for v, cost in neighbours[u]:
                    alt = dist[u] + cost
                    if alt < dist[v]:                                  # Relax (u,v,a)
                        dist[v] = alt
                        previous[v] = u
            #pp(previous)
            s, u = deque(), dest
            while previous[u]:
                s.pushleft(u)
                u = previous[u]
            s.pushleft(u)
            return s
     
     
    graph = Graph([("a", "b", 7),  ("a", "c", 9),  ("a", "f", 14), ("b", "c", 10),
                   ("b", "d", 15), ("c", "d", 11), ("c", "f", 2),  ("d", "e", 6),
                   ("e", "f", 9)])
    pp(graph.dijkstra("a", "e"))
    
    Output:
    
    ['a', 'c', 'd', 'e']

     rosettacode.org

    Понравилась статья? Поделить с друзьями:
  • Как найти нод чисел с 3 числами
  • Ошибка при запуске приложения плей маркет df dferh 01 как исправить
  • Как найти время работы теплового двигателя
  • Как найти предельная реализации
  • Как составить календарный план по дисциплине