алгоритм Дейкстры - на c++?

в течение последних четырех дней я пытаюсь понять алгоритм Дейкстры. Но я не могу. У меня есть вектор точек. Из этого я создал матрицу затрат. Но я не знаю, как сделать алгоритм Дейкстры. Источники доступны в сети, но я не из компьютерных наук, поэтому я не могу их понять. Я пытаюсь сделать такую функцию

vector<int> dijkstra(costMatrix[][])
{
  ....
  ....
  return vector<int>pathPointindex
}

main()
{
    vector<Point> availablePoints;
    costMatrix[][]=createCostMatrix();
    vector<int> indexes=dijikstra(costMatrix)
    for(int i=0;i<indexes.size();i++)
       cout << "path points are " << availablePoints[indexes[i]] << endl;
}

Если кто-нибудь, вы можете опубликовать код. Я не ленив. Но мой проект уже пересек крайний срок. сутки назад. Теперь я потерял надежду понять логику. Теперь я просто хочу функцию. "Человек в нужде и есть ангел" .

EDIT: особая благодарность "Локи астари" за его отличный ответ

6 ответов


советую посмотреть TopCoder учебник, который имеет очень прагматичный apporach. Вам нужно будет узнать, как работает очередь приоритетов STL и убедитесь, что у вас нет negative edge weights в свой график.

достойная полная реализация может быть найдена здесь. Вам нужно будет добавить к нему вектор пути и реализовать RecoverPath способ для того, чтобы получить узлы на пути от источника к приемнику. Чтобы использовать это решение, вам также необходимо преобразовать ваш adjacency matrix to adjacency list in следующий путь:

for (int i=0;i<nNodes;i++)
    for (int j=0;j<nNodes;j++){
        if (costMatrix[i][j] != NO_EDGE_VALUE){
            G[i].pb(make_pair(j,costMatrix[i],[j]));
        }
    }

EDIT: если ваш график плотный, я бы посоветовал вам использовать Форда Беллмана алгоритм намного проще и не должен быть намного медленнее.

EDIT2: чтобы вычислить путь, вы должны добавить в заголовок

int P[MAX]; /*array with links to parents*/
for(i=0; i<=nodes; i++) P[i] = -1; /*magic unset value*/

// dijkstra
while(!Q.empty()) {
    ....
    if(!F[v] && D[u]+w < D[v]) {
        D[v] = D[u] + w;
        /*By setting P[v] value we will remember what is the 
          previous node on path from source */
        P[v] = u; // <-- added line
        Q.push(pii(v, D[v]));
    }
    ...
}

тогда вам нужно добавить метод RecoverPath (он работает только тогда, когда существует путь)

vector<int> RecoverPath(int src, int dest){
    vector<int> path;
    int v = dest;
    while (v != src) {
        path.push_back(v);
        v = P[v];
    }
    path.push_back(src);
    std::reverse(path.begin(),path.end());
    return path;
}

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

по-английски:

это алгоритм поиска кратчайшего пути из точки А в точку Б.
В вычислительных терминах мы упрощаем маршрут до графа, состоящего из узлов и Дуг. Каждый узел представляет промежуточную точку, в то время как каждая дуга соединяет два узла и имеет (неотрицательный) вес, представляющий стоимость пересечения между двумя узлами.

для реализации алгоритма необходимо два списка:

  • закончил: Список (узел, стоимость), где вы рассчитали минимальную стоимость для достижения.
  • работает: отсортированный список (узел,стоимость), которые были проверены.
:
working.addNode(Start, 0); // No cost to get to start.

for( (node, cost) = working.popHead(); node != End; (node,cost) = working.popHead())
{
    // If we have already processed this node ignore it.
    if (finished.find(node))
    {    continue;
    }

    // We have just removed a node from working.
    // Because it is the top of the list it is guaranteed to be the shortest route to
    // the node. If there is another route to the node it must go through one of the
    // other nodes in the working list which means the cost to reach it will be higher
    // (because working is sorted). Thus we have found the shortest route to the node.

    // As we have found the shortest route to the node save it in finished.
    finished.addNode(node,cost);

    // For each arc leading from this node we need to find where we can get to.
    foreach(arc in node.arcs())
    {
        dest = arc.dest();
        if (NOT (finished.find(dest)))
        {
            // If the node is already in finished then we don't need to worry about it
            // as that will be the shortest route other wise calculate the cost and add
            // this new node to the working list.
            destCost = arc.cost() + cost;
            working.addNode(dest,destCost); // Note. Working is sorted list
        }
    }
} 

так что если вы думаете об этом алгоритме. Допустим, вы едете из Лондона в Манчестер.

finished = {} // empty.
working  = { (London,0) }

используя следующую матрицу затрат:

                  L    S    O    B    N    M    W
(L) ondon         -    50   60   100  130  -    -
(S) outhampton    50   -    70   -    -    -    -
(O) xford         60   70   -    50   -    200  -
(B) irmingham     100  -    50   -    -    80   110
(N) orwich        130  -    -    -    -    -    -
(M) anchester     -    -    200  80   -    -    80
Ne(W) castle      -    -    -    110  -    80   -

теперь вы берете Лондон из рабочего списка (как он находится во главе) и помещаете его в готовый список. Затем добавьте в рабочий список все города, непосредственно связанные с Лондоном.

finished = { (London,0) }
working  = { (Southampton, 50), (Oxford, 60), (Birmingham, 100), (Norwich,130) }

рассмотрим города в рабочем наборе внешний край пузыря, который расширился из Лондона. Задача алгоритма Дейкстры - продолжать расширять пузырь, пока мы не достигнем Манчестера (без каких-либо шагов, которые мы уже предприняли). Таким образом, пузырь всегда расширяется наружу, и мы всегда расширяем ту часть пузыря, которая наименьшая.

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

finished = { (London,0), (Southampton,50) }
working  = { (Oxford, 60), (Birmingham, 100), (Oxford, 120), (Norwich,130) }

Итак, повторите цикл. Возглавляет список Оксфорд. Из Оксфорда мы можем отправиться в Манчестер (200), Бирмингем (50) или обратно в Лондон (60) или Саутгемптон (помните, что нам нужно добавить стоимость поездки в Оксфорд к каждой из этих затрат выше. Обратите внимание, что из Оксфорда мы могли бы поехать в Саутгемптон, но мы уже нашли самый короткий маршрут в Саутгемптон, поэтому обработка не требуется) это оставит нас с:

finished = { (London,0), (Southampton,50), (Oxford, 60) }
working  = { (Birmingham, 100), (Birmingham,110), (Oxford, 120), (Norwich,130), (Manchester,200)}

обратите внимание, что у нас есть Манчестер в рабочем списке сейчас (это наш пункт назначения). Но нам нужно продолжать работать, как мы можем найти более короткий путь. Итак, теперь мы расширяемся из Бирмингема. Оттуда мы можем отправиться в Оксфорд (50), Манчестер(80), Лондон(100), Ньюкасл(110). Добавление стоимости добраться до Бирмингема в первую очередь это дает нам:

finished = { (London,0), (Southampton,50), (Oxford, 60), (Birmingham, 100) }
working  = { (Birmingham,110), (Oxford, 120), (Norwich,130), {Manchester, 180), (Manchester,200), (Newcastle, 210)}

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


основная идея алгоритма Дейкстры довольно проста: Предположим, у вас есть множество точек с известными кратчайшими путями к данной точке A. Тогда предположим, что мы хотим добавить новую точку C в множество. Давайте найдем, какие точки из множества связаны с точкой, которую мы хотим добавить. Пусть это будет точка B(i) Таким образом, для всех точек B(i) мы найдем сумму расстояний между A до B(i) и B(i) и C. наименьшее из этих расстояний будет минимальным между A и C.


#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <limits> 
#include <set>
#include <utility>
#include <algorithm> 
#include <iterator>

using namespace std;


typedef int vertex_t;
typedef double weight_t;

const weight_t max_weight = numeric_limits<double>::infinity();

struct neighbor {
    vertex_t target;
    weight_t weight;
    neighbor(vertex_t arg_target, weight_t arg_weight)
        : target(arg_target), weight(arg_weight) { }
};

typedef vector<vector<neighbor> > adjacency_list_t;

// Computing the shortest pathway

void
DijkstraComputePaths(vertex_t source,
                     const adjacency_list_t &adjacency_list,
                     vector<weight_t> &min_distance,
                     vector<vertex_t> &previous)
{
    int n = adjacency_list.size();
    min_distance.clear();
    min_distance.resize(n, max_weight);
    min_distance[source] = 0;
    previous.clear();
    previous.resize(n, -1);
    set<pair<weight_t, vertex_t> > vertex_queue;
    vertex_queue.insert(make_pair(min_distance[source], source));

    while (!vertex_queue.empty())
    {
        weight_t dist = vertex_queue.begin()->first;
        vertex_t u = vertex_queue.begin()->second;
        vertex_queue.erase(vertex_queue.begin());

        // Visit each edge exiting u
        const vector<neighbor> &neighbors = adjacency_list[u];
        for (vector<neighbor>::const_iterator neighbor_iter = neighbors.begin();
            neighbor_iter != neighbors.end();
            neighbor_iter++)
        {
            vertex_t v = neighbor_iter->target;
            weight_t weight = neighbor_iter->weight;
            weight_t distance_through_u = dist + weight;
            if (distance_through_u < min_distance[v]) {
                vertex_queue.erase(make_pair(min_distance[v], v));

                min_distance[v] = distance_through_u;
                previous[v] = u;
                vertex_queue.insert(make_pair(min_distance[v], v));

            }
        }
    } // while
}

реализация в C ++

#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
using namespace std;

#define pb push_back
#define mp make_pair
#define MAXN 50100
#define INF 1000000000

int N, M, d[MAXN]; vector<int> G[MAXN], C[MAXN];
set< pair<int, int> > T;

void solve(void)
{
    int i, j, k, val, x;

    for(i = 2; i <= N; i++) d[i] = INF;
    T.insert( mp(0, 1) );

    while( T.size() > 0 )
    {
        val = (*T.begin()).first, x = (*T.begin()).second;
        T.erase(*T.begin());
        for(i = 0; i < G[x].size(); i++)
         if(d[ G[x][i] ] > val + C[x][i] )
            d[ G[x][i] ] = val + C[x][i], T.insert(mp(d[G[x][i]],G[x][i]));
    }
}

int main(void)
{
    freopen("dijkstra.in", "rt", stdin);
    freopen("dijkstra.out", "wt", stdout);

    int i, a, b, c;

    scanf("%d %d\n", &N, &M);

    for(i = 1; i <= M; i++)
        scanf("%d %d %d\n", &a, &b, &c), G[a].pb(b), C[a].pb(c);

    solve();

    for(i = 2; i <= N; i++)
        printf("%d ", d[i] == INF ? 0 : d[i]);

    return 0;
}

#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>


using namespace std;

const size_t INT_MAX = 0xFFFFFFFF; // or any other value representing infinite distance.

сначала создайте ребро структуры, содержащее индекс исходного узла, индекс целевого узла и ребро "вес"( длина ).

struct edge { size_t from; size_t to; size_t length; };

определите узел класса, содержащий ребра для соседних соседей.

class Node 
{ 
public:
    void AddNeighborEdge( edge _NeighborEdge ) { m_neighborsEdges.push_back( _NeighborEdge ); } 
    vector<edge>::iterator FirstNeighborEdge() { return  m_neighborsEdges.begin(); }
    vector<edge>::iterator LastNeighborEdge() { return  m_neighborsEdges.end(); }

private: 
     vector<edge>  m_neighborsEdges; 
};

класс NeighborsDistanceUpdator будет использоваться в качестве" функтора " алгоритмом for_each для итеративного прохода и обновления минимального расстояния от текущего узла в графике до соседних соседей.

class NeighborsDistanceUpdator
{
public:
    NeighborsDistanceUpdator( vector<size_t>& _min_distance_from_source, queue< size_t >& _nodes_to_visit ) : m_min_distance_from_source( _min_distance_from_source ),                                                              m_nodes_to_visit( _nodes_to_visit ) 
                                                            {} 
    void operator()( edge& _edge )
    {   
        size_t from = _edge.from;
        size_t to = _edge.to;

        if ( m_min_distance_from_source[ to ] > m_min_distance_from_source[ from ] + _edge.length ) 
        {
            m_min_distance_from_source[ to ] = m_min_distance_from_source[ from ] + _edge.length;
            m_nodes_to_visit.push( to );
        }
    }    

private:
    vector<size_t>& m_min_distance_from_source;
    queue< size_t >& m_nodes_to_visit;
};

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

size_t dijkstra( map< size_t, Node  >& _graph, size_t _sourceIndex, size_t _targetIndex ) 
{
    vector<size_t> min_distance_from_source( _graph.size(), INT_MAX );
    min_distance_from_source[ _sourceIndex ] = 0;
    queue< size_t > nodes_to_visit;
    nodes_to_visit.push( _sourceIndex );
    NeighborsDistanceUpdator neighborsDistanceUpdator( min_distance_from_source, nodes_to_visit );

    while ( ! nodes_to_visit.empty() ) 
    {

        size_t currNodeIndex = nodes_to_visit.front();

        if ( currNodeIndex ==  _targetIndex ) return min_distance_from_source[ currNodeIndex ];

        nodes_to_visit.pop();

        vector<edge>::iterator firstNeighborEdge= _graph[ currNodeIndex ].FirstNeighborEdge();
        vector<edge>::iterator lastNeighborEdge= _graph[ currNodeIndex ].LastNeighborEdge();

        for_each( firstNeighborEdge, lastNeighborEdge, neighborsDistanceUpdator );
    }
    return INT_MAX;
}