Dijkstra 算法 C++实现(邻接表)

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值