[英]dijkstra's algorithm - in c++?
在過去的四天里,我試圖了解dijkstra的算法。 但我不能。 我有一個點矢量。 從那以后我創建了一個成本矩陣。 但我不知道如何制作dijkstra的算法。 來源有網,但我不是來自計算機科學背景,所以我無法理解它們。 我正在嘗試制作這樣的功能
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;
}
如果有人,請您發布代碼。 我不懶。 但是我的項目已經在一天前跨越了截止日期。 現在我失去了理解邏輯的希望。 現在我想要這個功能。 “有需要的人確實是天使”。
編輯:特別感謝“Loki astari”的出色回答
這是一種用於查找從A點到B點的最短路徑的算法。
在計算術語中,我們簡化了由節點和弧組成的圖形的路徑。 每個節點表示一個中間點,而每個弧連接兩個節點,並且具有(非負)權重,表示在兩個節點之間遍歷的成本。
要實現該算法,您需要兩個列表:
算法:
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 -
使用以下Costs矩陣:
finished = { (London,0) }
working = { (Southampton, 50), (Oxford, 60), (Birmingham, 100), (Norwich,130) }
現在,您將倫敦從工作列表中取出(因為它在頭部)並將其放入完成的列表中。 然后將所有直接連接到倫敦的城鎮添加到工作清單中。
finished = { (London,0) } working = { (Southampton, 50), (Oxford, 60), (Birmingham, 100), (Norwich,130) }
考慮工作區中的城鎮是從倫敦擴展的泡沫的外緣。 Dijkstra算法的工作是繼續擴大泡沫,直到我們擊中曼徹斯特(沒有回顧我們已經采取的任何步驟)。 因此,泡沫總是向外擴展,我們總是擴大泡沫中最小的部分。
因此,下一步是采取列表的頭部並重復。 從南安普敦出發,只有兩個目的地。 回到倫敦(我們在完成的清單中將其丟棄)和牛津。 到達牛津的費用是從南安普敦到牛津的費用是50+(因此請注意它在工作清單中兩次,但不要擔心我們以后會丟棄它作為最佳路線)。
finished = { (London,0), (Southampton,50), (Oxford, 60) }
working = { (Birmingham, 100), (Birmingham,110), (Oxford, 120), (Norwich,130), (Manchester,200)}
所以重復循環。 名單的負責人是牛津大學。 從牛津出發,我們可以去曼徹斯特(200),伯明翰(50)或回到倫敦(60)或南安普敦(請記住,我們需要增加牛津到上述每項費用的成本。請注意,從牛津我們可以有去了南安普敦但是我們已經找到了通往南安普敦的最短路線,所以不需要處理。這將給我們留下:
finished = { (London,0), (Southampton,50), (Oxford, 60), (Birmingham, 100) }
working = { (Birmingham,110), (Oxford, 120), (Norwich,130), {Manchester, 180), (Manchester,200), (Newcastle, 210)}
請注意,我們現在在工作列表中有曼徹斯特(這是我們的目的地)。 但我們需要繼續努力,因為我們可能會找到更短的路線。 所以現在我們從伯明翰擴展。 從那里我們可以去牛津(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英里,否則我們將在此后的迭代中到達曼徹斯特。
我建議你看一下具有非常實用的apporach的TopCoder教程。 您需要了解STL優先級隊列的工作原理,並確保圖形中沒有任何negative edge weights
。
可在此處找到體面的完整實施。 您必須向其添加Path向量並實現RecoverPath
方法,以便從源到接收器獲取路徑上的節點。 要使用此解決方案,您還需要通過以下方式將adjacency matrix
轉換為adjacency list
:
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]));
}
}
編輯:如果您的圖表密集,我會建議您使用福特貝爾曼算法更簡單,不應該慢得多。
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;
}
#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
}
Dijkstra算法的主要思想非常簡單:假設你有一組具有到達給定點A的已知最短路徑的點。然后我們假設我們想要為一個集合添加一個新的點C. 讓我們找到集合中的哪些點與我們想要添加的點相關聯。 讓它成為點B(i)因此,對於所有點B(i),我們將新找到A到B(i)和B(i)和C之間的距離之和。這些距離中最小的距離是A和C.
用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; };
定義一個包含相鄰鄰居邊的Node節點。
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;
};
class 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;
};
至於dijkstra算法,只需在圖中的所有節點上運行,並為每個節點更新距離源的最小距離(如果更少),同時保存要訪問的相鄰節點。
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;
}
測試...
int main()
{
Node node1;
Node node2;
Node node3;
Node node4;
map< size_t, Node > graph;
edge ed;
ed.from = 0;
ed.to = 1;
ed.length = 1;
node1.AddNeighborEdge( ed );
cout << "node: " << 0 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 0;
ed.to = 2;
ed.length = 4;
node1.AddNeighborEdge( ed );
graph.insert( make_pair( 0, node1 ) );
cout << "node: " << 0 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 1;
ed.to = 2;
ed.length = 1;
node2.AddNeighborEdge( ed );
cout << "node: " << 1 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 1;
ed.to = 3;
ed.length = 3;
node2.AddNeighborEdge( ed );
graph.insert( make_pair( 1, node2 ) );
cout << "node: " << 1 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 2;
ed.to = 3;
ed.length = 1;
node3.AddNeighborEdge( ed );
graph.insert( make_pair( 2, node3 ) );
cout << "node: " << 2 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
ed.from = 3;
ed.to = INT_MAX;
ed.length = INT_MAX;
node3.AddNeighborEdge( ed );
graph.insert( make_pair( 3, node4 ) );
cout << "node: " << 2 << " to: " << ed.to ;
cout << " lenth: " << ed.length << endl << endl;
cout << "min length from: 1 to 4 = " << dijkstra( graph, 0,3 ) << endl;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.