Dijkstra 用于寻找最短路径。用最短的时间从起点走到终点。
1.创建邻接表
我感觉邻接表和散列表(hash table) 很像,用一个map或者struct先创建头部节点(顶点)索引,之后再给边创建链表。
map<string, ArcNode*> ADJList;//也可以用结构体数组储存顶点
struct ArcNode {
string NextNode;//边的另一端点
double EdgeLength;//边长
ArcNode* next;
};
使用的map.txt:
//map.txt
8 13
A - B : 1
A - E : 1
B - E : 6
B - F : 4
B - C : 2
E - F : 6
F - C : 1
F - G : 4
C - D : 2
C - G : 5
G - D : 2
G - H : 1
D - H : 3
邻接表实现具体代码:
struct GraphInfo {//make a pack for passing parameters
map<string, ArcNode*> ADJList;
int Vertex, Arc;
};
ArcNode* CreateNewArcNode(string NN, double EL) {
ArcNode* tmp = new ArcNode();
tmp -> NextNode = NN;
tmp -> EdgeLength = EL;
tmp -> next = NULL;
return tmp;
}
GraphInfo* ReadMap(string& mapdir) {
ifstream in(mapdir, ifstream::in);
if(!in.is_open()) {
cerr << "Cannot open input file: " << mapdir << endl;
exit(EXIT_FAILURE);
}
string l;
int vertex, edge;
getline(in, l);
istringstream iss(l);
iss >> vertex >> edge;
getline(in, l);//empty line
map<string, ArcNode*>VertMap;
for (int i=0; i<edge; i++) {
getline(in, l);
string start, end;
double Distance;
int fst = l.find('-');
int sec = l.find(':');
start = l.substr(0, fst-1);
end = l.substr(fst+2, sec-fst-3);
istringstream sss((l.substr(sec+2, l.length()-sec-2)));
sss >> Distance;
if (VertMap.find(start) != VertMap.end()) {
ArcNode* tmp = CreateNewArcNode(end, Distance);
tmp -> next = VertMap[start];
VertMap[start] = tmp;
}
else {
VertMap.insert(pair<string, ArcNode*>(start, CreateNewArcNode(end, Distance)));
}
if(VertMap.find(end) != VertMap.end()) {
ArcNode* tmp = CreateNewArcNode(start, Distance);
tmp -> next = VertMap[end];
VertMap[end] = tmp;
}
else {
VertMap.insert(pair<string, ArcNode*>(end, CreateNewArcNode(start, Distance)));
}
}
GraphInfo* temp = new GraphInfo();
temp -> ADJList = VertMap;
temp -> Vertex = vertex;
temp -> Arc = edge;
return temp;
}
具体就是新出现的顶点就加入map里,出现过的顶点就往后面链接新节点。(做了无向图,一条路线可以往返)
打印邻接表代码:
void PrintMap(GraphInfo* Graph) {
for (auto it : (Graph->ADJList)) {
cout << it.first;
if(it.second != NULL) {
ArcNode* tmp = it.second;
while(tmp != NULL) {
cout << " -> " << tmp -> NextNode << tmp -> EdgeLength;
tmp = tmp -> next;
}
}
cout << endl;
}
}
//result
A -> E1 -> B1
B -> C2 -> F4 -> E6 -> A1
C -> G5 -> D2 -> F1 -> B2
D -> H3 -> G2 -> C2
E -> F6 -> B6 -> A1
F -> G4 -> C1 -> E6 -> B4
G -> H1 -> D2 -> C5 -> F4
H -> D3 -> G1
2.算法实现
首先,Dijkstra无法解决负边权图问题,需要使用 Bellman-Ford s算法
Dij.主要需要三种表,邻接表/邻接矩阵(储存了地图的基本信息)、距离(每个点到起点的最短距离)、父节点(储存每个顶点的上个顶点)。
地图在上面已经实现。
struct Dijkastra {
map<string, double> Distances;//costs 距离
map<string, string> PrevNode;//parents 父节点
map<string, bool> processed;//记录已处理顶点
};
思路是:loop:(只要还有未处理的节点 -> 获取距离起点最近的点 -> 更新与其相关的顶点的距离 -> 如果有顶点被更新,同时更新其父节点 -> 记录该节点为处理过)
map<string, bool>::iterator it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
string Node = FindLowestCost(Maps -> Distances, Maps -> processed);
while (it != Maps -> processed.end()) {
/*Node: current Node
*tmp -> NextNode: Arc (next Node)
*tmp -> EdgeLength: Distance
*/
ArcNode* tmp = (Graph -> ADJList)[Node];
while(tmp != NULL) {
if(tmp->EdgeLength + Maps -> Distances[Node] < Maps -> Distances[tmp->NextNode]) {
Maps -> Distances[tmp->NextNode] = (tmp->EdgeLength + Maps -> Distances[Node]);
Maps -> PrevNode[tmp->NextNode] = Node;
}
tmp = tmp -> next;
}
Maps -> processed[Node] = true;
Node = FindLowestCost(Maps -> Distances, Maps -> processed);
it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
//可加可不加
/*if(Maps -> processed[des] == true) {
break;
}*/
}
通过遍历找到离起点最近的节点(同时保证其未处理)
string FindLowestCost(map<string, double> costs, map<string, bool> proceed) {
double low = MAX_DIS;
string node = "\0";
for(auto iter : costs) {
if((iter.second < low) && (proceed[iter.first] == false)) {
low = iter.second;
node = iter.first;
}
}
return node;
}
已处理节点的查找器:
class finder
{
public:
finder(const bool& x) : s(x) {}
bool operator ()(const map<string, bool>::value_type& item)
{
return item.second == s;
}
private:
const bool& s;
};
//用法:map<string, bool>::iterator it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
算法整体代码:
#define MAX_DIS 1000000.0
//implement algorithm
struct Dijkastra {
map<string, double> Distances;//costs
map<string, string> PrevNode;//parents
map<string, bool> processed;
};
class finder
{
public:
finder(const bool& x) : s(x) {}
bool operator ()(const map<string, bool>::value_type& item)
{
return item.second == s;
}
private:
const bool& s;
};
Dijkastra* InitDijStruct(map<string, ArcNode*> ADList, int vertex) {
Dijkastra* tmp = new Dijkastra();
for (auto it : ADList) {
(tmp -> Distances).insert(pair<string, double>(it.first, MAX_DIS));
(tmp -> PrevNode).insert(pair<string, string>(it.first, "\0"));
(tmp ->processed).insert(pair<string, bool> (it.first, false));
}
return tmp;
}
string FindLowestCost(map<string, double> costs, map<string, bool> proceed) {
double low = MAX_DIS;
string node = "\0";
for(auto iter : costs) {
if((iter.second < low) && (proceed[iter.first] == false)) {
low = iter.second;
node = iter.first;
}
}
return node;
}
Dijkastra* DijPath(GraphInfo* Graph, string start, string des) {
Dijkastra* Maps = InitDijStruct(Graph -> ADJList, Graph->Vertex);
Maps -> Distances[start] = 0;
Maps -> PrevNode[start] = start;
map<string, bool>::iterator it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
string Node = FindLowestCost(Maps -> Distances, Maps -> processed);
while (it != Maps -> processed.end()) {
/*Node: current Node
*tmp -> NextNode: Arc (next Node)
*tmp -> EdgeLength: Distance
*/
ArcNode* tmp = (Graph -> ADJList)[Node];
while(tmp != NULL) {
if(tmp->EdgeLength + Maps -> Distances[Node] < Maps -> Distances[tmp->NextNode]) {
Maps -> Distances[tmp->NextNode] = (tmp->EdgeLength + Maps -> Distances[Node]);
Maps -> PrevNode[tmp->NextNode] = Node;
}
tmp = tmp -> next;
}
Maps -> processed[Node] = true;
Node = FindLowestCost(Maps -> Distances, Maps -> processed);
it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
if(Maps -> processed[des] == true) {
break;
}
}
return Maps;
}
打印路径:
void PrintPath(Dijkastra* Algo, string start, string dest) {
stack<string> Route;
string tmp = dest;
map<string, string> parents = Algo -> PrevNode;
while(tmp != start) {
Route.push(tmp);
tmp = parents[tmp];
}
cout << start;
while(!Route.empty()) {
cout << " -> " << Route.top();
Route.pop();
}
cout << " (" << (Algo -> Distances)[dest] << ")";
cout << endl;
}
效果:
比如我打印一个从B到H的路径
$ bin/dijkstraSP Maps/map1.txt B H
>> B -> C -> D -> H (7)
整体代码:
common.h
#include <iostream>
#include <fstream>
#include <map>
#include <sstream>
#include <vector>
#include <string>
#include<map>
#include<queue>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
Map.h
#ifndef GMAP_H
#define GMAP_H
#include "../include/common.h"
struct ArcNode {
string NextNode;
double EdgeLength;
ArcNode* next;
};
struct GraphInfo {//make a pack for passing parameters
map<string, ArcNode*> ADJList;
int Vertex, Arc;
};
ArcNode* CreateNewArcNode(string NN, double EL) {
ArcNode* tmp = new ArcNode();
tmp -> NextNode = NN;
tmp -> EdgeLength = EL;
tmp -> next = NULL;
return tmp;
}
GraphInfo* ReadMap(string& mapdir) {
ifstream in(mapdir, ifstream::in);
if(!in.is_open()) {
cerr << "Cannot open input file: " << mapdir << endl;
exit(EXIT_FAILURE);
}
string l;
int vertex, edge;
getline(in, l);
istringstream iss(l);
iss >> vertex >> edge;
getline(in, l);//empty line
map<string, ArcNode*>VertMap;
for (int i=0; i<edge; i++) {
getline(in, l);
string start, end;
double Distance;
int fst = l.find('-');
int sec = l.find(':');
start = l.substr(0, fst-1);
end = l.substr(fst+2, sec-fst-3);
istringstream sss((l.substr(sec+2, l.length()-sec-2)));
sss >> Distance;
if (VertMap.find(start) != VertMap.end()) {
ArcNode* tmp = CreateNewArcNode(end, Distance);
tmp -> next = VertMap[start];
VertMap[start] = tmp;
}
else {
VertMap.insert(pair<string, ArcNode*>(start, CreateNewArcNode(end, Distance)));
}
if(VertMap.find(end) != VertMap.end()) {
ArcNode* tmp = CreateNewArcNode(start, Distance);
tmp -> next = VertMap[end];
VertMap[end] = tmp;
}
else {
VertMap.insert(pair<string, ArcNode*>(end, CreateNewArcNode(start, Distance)));
}
}
GraphInfo* temp = new GraphInfo();
temp -> ADJList = VertMap;
temp -> Vertex = vertex;
temp -> Arc = edge;
return temp;
}
void PrintMap(GraphInfo* Graph) {
for (auto it : (Graph->ADJList)) {
cout << it.first;
if(it.second != NULL) {
ArcNode* tmp = it.second;
while(tmp != NULL) {
cout << " -> " << tmp -> NextNode << tmp -> EdgeLength;
tmp = tmp -> next;
}
}
cout << endl;
}
}
#endif //GMAP_H
Dijkstra.h
#ifndef DIJKSTRASP_H
#define DIJKSTRASP_H
#include "../include/common.h"
#include "GMap.h"
#define MAX_DIS 1000000.0
//implement algorithm
struct Dijkastra {
map<string, double> Distances;//costs
map<string, string> PrevNode;//parents
map<string, bool> processed;
};
class finder
{
public:
finder(const bool& x) : s(x) {}
bool operator ()(const map<string, bool>::value_type& item)
{
return item.second == s;
}
private:
const bool& s;
};
Dijkastra* InitDijStruct(map<string, ArcNode*> ADList, int vertex) {
Dijkastra* tmp = new Dijkastra();
for (auto it : ADList) {
(tmp -> Distances).insert(pair<string, double>(it.first, MAX_DIS));
(tmp -> PrevNode).insert(pair<string, string>(it.first, "\0"));
(tmp ->processed).insert(pair<string, bool> (it.first, false));
}
return tmp;
}
string FindLowestCost(map<string, double> costs, map<string, bool> proceed) {
double low = MAX_DIS;
string node = "\0";
for(auto iter : costs) {
if((iter.second < low) && (proceed[iter.first] == false)) {
low = iter.second;
node = iter.first;
}
}
return node;
}
Dijkastra* DijPath(GraphInfo* Graph, string start, string des) {
Dijkastra* Maps = InitDijStruct(Graph -> ADJList, Graph->Vertex);
Maps -> Distances[start] = 0;
Maps -> PrevNode[start] = start;
map<string, bool>::iterator it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
string Node = FindLowestCost(Maps -> Distances, Maps -> processed);
while (it != Maps -> processed.end()) {
/*Node: current Node
*tmp -> NextNode: Arc (next Node)
*tmp -> EdgeLength: Distance
*/
ArcNode* tmp = (Graph -> ADJList)[Node];
while(tmp != NULL) {
if(tmp->EdgeLength + Maps -> Distances[Node] < Maps -> Distances[tmp->NextNode]) {
Maps -> Distances[tmp->NextNode] = (tmp->EdgeLength + Maps -> Distances[Node]);
Maps -> PrevNode[tmp->NextNode] = Node;
}
tmp = tmp -> next;
}
Maps -> processed[Node] = true;
Node = FindLowestCost(Maps -> Distances, Maps -> processed);
it = find_if(Maps -> processed.begin(), Maps -> processed.end(), finder(false));
if(Maps -> processed[des] == true) {
break;
}
}
return Maps;
}
void PrintPath(Dijkastra* Algo, string start, string dest) {
stack<string> Route;
string tmp = dest;
map<string, string> parents = Algo -> PrevNode;
while(tmp != start) {
Route.push(tmp);
tmp = parents[tmp];
}
cout << start;
while(!Route.empty()) {
cout << " -> " << Route.top();
Route.pop();
}
cout << " (" << (Algo -> Distances)[dest] << ")";
cout << endl;
}
#endif //DIJKSTRASP_H
main.cpp
#include "../include/common.h"
#include "GMap.h"
#include "DijkstraSP.h"
int main(int argc, char* argv[]) {
// Read CLI to get map, source, destination
if (argc < 4) {
cerr << "Usage: " << argv[0] << " <map> <source> <destination(s)>" << endl;
exit(EXIT_FAILURE);
}
string maps = argv[1];//map name
string source = argv[2];//start point
string destination = argv[3];//end point
// Read Map
GraphInfo* x;
x = ReadMap(maps);
//Print Adjacency list
//PrintMap(x);
// Run Dijkstra's Algo to find shortest path
Dijkastra* y = DijPath(x, source, destination);
// Print shortest path
PrintPath(y, source, destination);
return 0;
}
Makefile
CC := g++
STD := -std=c++11
CFLAGS := -Wall -Werror
dijkstraSP: src/main.cpp #$make
mkdir -p bin
$(CC) $(STD) $(CFLAGS) -o bin/$@ $^
clean: #$make clean
rm -rf bin
fin.