C/C++利用迪杰斯特拉最短路径算法,求解千万结点道路网最短路径

本文主要是介绍C/C++利用迪杰斯特拉最短路径算法,求解千万结点道路网最短路径,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《算法导论》dijkstra算法的图,怎么图是歪的?顺便治疗一下颈椎病,嘎嘎!

计算机程序很笨,一开始它看不到整个图,只能从起点开始,慢慢的探索周围的点,由近及远,所以它是一个广度优先搜索。

很自然的,程序刚开始,要把所有点的distance值(从起点到该点的距离)设置为无穷大,意味所有点都是不可达的状态。 然后,我们要把起点的distance值设置为0。接下来,程序进入循环,循环里面做3件事:

(1)从当前集合里找到distance值最小的点,准备进行处理。

(2)更新这个点的所有邻接点的distance值,更新的条件是什么呢?假设这个点是A点,它的distance值是50(从起点到A点的距离值是50),它有一个邻接点是B点,此前B点的distance值是100(从起点到B点的距离值是100),又假设A到B的距离只有10,那么我们发现:经过A到达B,有一条比之前100更短的路径,即50+10=60,这时候将B的distance值更新为60,意味着我们找到了一条从起点到达B点的更短的路径。(将这个点的邻接点放入集合,这一个操作要不要,取决于集合是怎么定义的)

(3)将这个处理过的A点从集合中删除。

这个算法有两个关键的事情:一是这个集合里的点的个数是如何增减的,二是如何在这个集合里找到distance值最小的点。

做到这两件事不难,但是要保证程序运行效率,需要考虑一些事。

第一,找最小值,用什么方法?所有数据排序?这种排序的目的是“让全体数据有序”,但实际上我们不需要这种“有序”,我们只需要找到那个最小的值就行。用遍历一遍的方法,找到最小值?效率还是低了。最后我们用小根堆来找到最小值,C++里可以自己建堆、调整堆,但是,可以直接用优先队列,速度还要快一点,优先队列它也是用堆实现的。

第二,集合如何定义?这个集合里装的是未处理过的结点,还是待处理的结点?这两者是有区别的。

下面使用老美网站上的一个数据集来测试最短路径程序。网址是:9th DIMACS Implementation Challenge: Shortest Paths

直接使用比较大的道路数据集,1400多万结点,3400多万条边。

先用C++的map,将原始的边数据转为邻接矩阵(这个代码略),而且分多个文件存储,回头可以多线程读取文件,加快文件读取速度,点的数据也分为多个文件存储。

上图数据的解释:从点0到点2516544有一条路径,长度是2025米,从点0到点576739有一条路径,长度是2617米,从点0到点1520470有一条路径,长度是520,也就是说从点0出去有3条路径;然后,从点1到点524282有一条路径,长度是2359……

程序运行结果,读文件到内存1.4秒,找到一个点到其它1400多万个点的最短路径,大概用时1.6秒:

路径有点长,所以保存在一个path.txt文件中:

代码如下:

#include <vector>
#include <queue>
#include <thread>
#include <limits>
#include <iostream>
using namespace std;#define THREADNUM 14
#define PINFINITY INT_MAXclass CVertex
{
public:int id;int distance;	//从源点到该点的距离int parent;     //记录最短路径经过的结点public:CVertex(int _id = -1, int _distance = -1, int _parent = -1){id = _id;distance = _distance;parent = _parent;}void operator=(const CVertex& vertex){id = vertex.id;distance = vertex.distance;parent = vertex.parent;}bool operator<(const CVertex& vertex){return this->distance < vertex.distance;}~CVertex(){}
};class CVexIdAndDistance
{
public:int id;int distance;public:CVexIdAndDistance(int _id = -1, int _distance = -1){id = _id;distance = _distance;}bool operator==(const CVexIdAndDistance& vexIdAndDistance){if (id == vexIdAndDistance.id && distance == vexIdAndDistance.distance){return true;}else{return false;}}~CVexIdAndDistance(){}
};class CEdge
{
public:int toVexId;int weight;public:CEdge(int _toVexId = -1, int _weight = 0){toVexId = _toVexId;weight = _weight;}
};class Cmp
{
public:bool operator() (const CVexIdAndDistance& a, const CVexIdAndDistance& b){return a.distance > b.distance;}
};int GetVertexNumThread(const char* vertexInfoFilePath, int* vertexNumPerFile, int tid);
int GetEdgeNumThread(const char* edgeInfoFilePath, int* edgeNum, int tid);
int GetVertexAndEdgeInfoThread(const char* vertexInfoFilePath, const char* edgeInfoFilePath, CVertex* vertex, CEdge** edge, int tid);
int UpdateDistance(CVertex* vertex, priority_queue<CVexIdAndDistance, vector<CVexIdAndDistance>, Cmp>& priorityQueueForSort, const int* edgeNum, const CEdge* const* edge, int srcVexId, int dstVexId);
int GetPath(const CVertex* vertex, int srcVexId, int dstVexId);char vertexInfoFilePath[50] = "e:\\USA-road-d.Central\\vertex";
char edgeInfoFilePath[50] = "e:\\USA-road-d.Central\\edge";
int totalVertexNum = 0;
int vertexNumPerThread = 0;int main()
{clock_t c[10] = { 0 };c[0] = clock();cout << THREADNUM << "线程从文件读取点和边数据" << endl;int all_vertex_file_size = 0;long long int all_edge_file_size = 0;FILE* fp = NULL;for (int i = 0; i < THREADNUM; i++){char vertexInfoFileName[50] = "";strcat_s(vertexInfoFileName, vertexInfoFilePath);char temp[5] = "";_itoa_s(i, temp, 10);strcat_s(vertexInfoFileName, temp);strcat_s(vertexInfoFileName, ".txt");fopen_s(&fp, vertexInfoFileName, "rb");if (fp == NULL)return -1;_fseeki64(fp, 0, SEEK_END);long long int filesize = _ftelli64(fp);all_vertex_file_size += filesize;if (fp != NULL)fclose(fp);char edgeInfoFileName[50] = "";strcat_s(edgeInfoFileName, edgeInfoFilePath);_itoa_s(i, temp, 10);strcat_s(edgeInfoFileName, temp);strcat_s(edgeInfoFileName, ".txt");fopen_s(&fp, edgeInfoFileName, "rb");if (fp == NULL)return -1;_fseeki64(fp, 0, SEEK_END);filesize = _ftelli64(fp);all_edge_file_size += filesize;if (fp != NULL)fclose(fp);}cout << "点文件大小:" << all_vertex_file_size / 1024.0 / 1024 / 1024 << " GB" << endl;cout << "边文件大小:" << all_edge_file_size / 1024.0 / 1024 / 1024 << " GB" << endl;int vertexNumPerFile[THREADNUM] = { 0 };thread td0[THREADNUM];for (int i = 0; i < THREADNUM; i++){td0[i] = thread(&GetVertexNumThread, vertexInfoFilePath, vertexNumPerFile, i);}for (int i = 0; i < THREADNUM; i++){td0[i].join();}for (int i = 0; i < THREADNUM; i++){totalVertexNum += vertexNumPerFile[i];}vertexNumPerThread = int((totalVertexNum / 1000000) / THREADNUM) * 1000000;CVertex* vertex = new CVertex[totalVertexNum];int* edgeNum = new int[totalVertexNum];memset(edgeNum, -1, totalVertexNum);int totalEdgeNum = 0;int srcVexId = -1, dstVexId = -1;thread td1[THREADNUM];for (int i = 0; i < THREADNUM; i++){td1[i] = thread(&GetEdgeNumThread, edgeInfoFilePath, edgeNum, i);}for (int i = 0; i < THREADNUM; i++){td1[i].join();}for (int i = 0; i < totalVertexNum; i++){totalEdgeNum += edgeNum[i];}CEdge** edge = new CEdge * [totalVertexNum];if (edge == NULL){return -1;}for (int i = 0; i < totalVertexNum; i++){edge[i] = NULL;}for (int i = 0; i < totalVertexNum; i++){if (edgeNum[i] != 0){edge[i] = new CEdge[edgeNum[i]];if (edge[i] == NULL){return -1;}}elseedge[i] = NULL;}thread td2[THREADNUM];for (int i = 0; i < THREADNUM; i++){td2[i] = thread(&GetVertexAndEdgeInfoThread, vertexInfoFilePath, edgeInfoFilePath, vertex, edge, i);}for (int i = 0; i < THREADNUM; i++){td2[i].join();}cout << "图中点数:" << totalVertexNum << endl;cout << "图中边数:" << totalEdgeNum << endl;c[1] = clock();cout << "用时:" << c[1] - c[0] << "毫秒" << endl;cout << "----------------------------------------" << endl;c[2] = clock();int maxOutDegree = 0;int imax = 0;for (int i = 0; i < totalVertexNum; i++){if (edgeNum[i] > maxOutDegree){maxOutDegree = edgeNum[i];imax = i;}}cout << "顶点" << imax << "具有最大出度:" << maxOutDegree << endl;c[3] = clock();cout << "用时:" << c[3] - c[2] << "毫秒" << endl;cout << "----------------------------------------" << endl;cout << "输入起点ID:";cin >> srcVexId;c[4] = clock();vertex[srcVexId].distance = 0;priority_queue<CVexIdAndDistance, vector<CVexIdAndDistance>, Cmp> priorityQueueForSort;priorityQueueForSort.push(CVexIdAndDistance(srcVexId, 0));UpdateDistance(vertex, priorityQueueForSort, edgeNum, edge, srcVexId, dstVexId);c[5] = clock();cout << "计算起点" << srcVexId << "到其它所有点的最短路径用时:" << c[5] - c[4] << "毫秒" << endl;cout << "----------------------------------------" << endl;while (true){cout << "输入终点ID:";cin >> dstVexId;if (dstVexId == -1){break;}c[6] = clock();GetPath(vertex, srcVexId, dstVexId);c[7] = clock();cout << "用时:" << c[7] - c[6] << "毫秒" << endl;cout << "----------------------------------------" << endl;}return 0;
}int GetVertexNumThread(const char* vertexInfoFilePath, int* vertexNumPerFile, int tid)
{char vertexInfoFileName[50] = "";strcat_s(vertexInfoFileName, vertexInfoFilePath);char temp[5] = "";_itoa_s(tid, temp, 10);strcat_s(vertexInfoFileName, temp);strcat_s(vertexInfoFileName, ".txt");FILE* fp;fopen_s(&fp, vertexInfoFileName, "rb");if (fp == NULL)return -1;char* line = new char[100];while (!feof(fp)){fgets(line, 100, fp);vertexNumPerFile[tid]++;}
}int GetEdgeNumThread(const char* edgeInfoFilePath, int* edgeNum, int tid)
{char edgeInfoFileName[50] = "";strcat_s(edgeInfoFileName, edgeInfoFilePath);char temp[5] = "";_itoa_s(tid, temp, 10);strcat_s(edgeInfoFileName, temp);strcat_s(edgeInfoFileName, ".txt");FILE* fp = NULL;fopen_s(&fp, edgeInfoFileName, "r");if (fp == NULL)return -1;char* line = new char[totalVertexNum * 2];int i, j;int lineIndex = 0;int tokenIndex = 0;while (!feof(fp)){fgets(line, totalVertexNum * 2, fp);tokenIndex = 0;i = 0;while (line[i] != '\0'){if (line[i] >= '0' && line[i] <= '9'){j = 0;while (line[i] >= '0' && line[i] <= '9'){i++;}tokenIndex++;}else{i++;}}edgeNum[vertexNumPerThread * tid + lineIndex] = tokenIndex / 2;lineIndex++;}
}int GetVertexAndEdgeInfoThread(const char* vertexInfoFilePath, const char* edgeInfoFilePath, CVertex* vertex, CEdge** edge, int tid)
{char vertexInfoFileName[50] = "";strcat_s(vertexInfoFileName, vertexInfoFilePath);char temp[5] = "";_itoa_s(tid, temp, 10);strcat_s(vertexInfoFileName, temp);strcat_s(vertexInfoFileName, ".txt");FILE* fp = NULL;fopen_s(&fp, vertexInfoFileName, "rb");if (fp == NULL)return -1;char* line = new char[totalVertexNum * 2];char t[50];int tokenIndex = 0;while (!feof(fp)){fgets(line, totalVertexNum * 2, fp);int i = 0;int j;while (line[i] != '\0'){if (line[i] >= '0' && line[i] <= '9'){j = 0;while (line[i] >= '0' && line[i] <= '9'){t[j++] = line[i++];}t[j] = '\0';vertex[vertexNumPerThread * tid + tokenIndex].id = atoi(t);tokenIndex++;}else{i++;}}}if (tid < THREADNUM - 1){for (int i = 0; i < vertexNumPerThread; i++){vertex[vertexNumPerThread * tid + i].distance = PINFINITY;}}else{for (int i = 0; i < totalVertexNum - vertexNumPerThread * THREADNUM + vertexNumPerThread; i++){vertex[vertexNumPerThread * tid + i].distance = PINFINITY;}}if (fp != NULL)fclose(fp);char edgeInfoFileName[50] = "";strcat_s(edgeInfoFileName, edgeInfoFilePath);_itoa_s(tid, temp, 10);strcat_s(edgeInfoFileName, temp);strcat_s(edgeInfoFileName, ".txt");fopen_s(&fp, edgeInfoFileName, "rb");if (fp == NULL)return -1;int i, j;int lineIndex = 0;tokenIndex = 0;while (!feof(fp)){fgets(line, totalVertexNum * 2, fp);tokenIndex = 0;i = 0;while (line[i] != '\0'){if (line[i] >= '0' && line[i] <= '9'){j = 0;while (line[i] >= '0' && line[i] <= '9'){t[j++] = line[i++];}t[j] = '\0';if (tokenIndex % 2 == 0){edge[vertexNumPerThread * tid + lineIndex][tokenIndex / 2].toVexId = atoi(t);}else{edge[vertexNumPerThread * tid + lineIndex][tokenIndex / 2].weight = atoi(t);}tokenIndex++;}else{i++;}}lineIndex++;}if (fp != NULL)fclose(fp);return 0;
}int UpdateDistance(CVertex* vertex, priority_queue<CVexIdAndDistance, vector<CVexIdAndDistance>, Cmp>& priorityQueueForSort, const int* edgeNum, const CEdge* const* edge, int srcVexId, int dstVexId)
{CVexIdAndDistance currentVertex;vector<CVexIdAndDistance>::iterator iter;while (true){if (priorityQueueForSort.size() == 0){break;}currentVertex = priorityQueueForSort.top();priorityQueueForSort.pop();for (int i = 0; i < edgeNum[currentVertex.id]; i++){if (edgeNum[currentVertex.id] == 0){continue;}int toVexId = edge[currentVertex.id][i].toVexId;if (currentVertex.distance + edge[currentVertex.id][i].weight < vertex[toVexId].distance){vertex[toVexId].distance = currentVertex.distance + edge[currentVertex.id][i].weight;vertex[toVexId].parent = currentVertex.id;priorityQueueForSort.push(CVexIdAndDistance(toVexId, vertex[toVexId].distance));}}}return 0;
}int GetPath(const CVertex* vertex, int srcVexId, int dstVexId)
{if (vertex[dstVexId].distance == PINFINITY){cout << endl << "no path to the vertex " << dstVexId << endl;return -1;}int* path = new int[totalVertexNum];memset(path, -1, totalVertexNum);int i = 0;int tid = dstVexId;while (tid != -1){path[i++] = tid;tid = vertex[tid].parent;if (tid == srcVexId){break;}}path[i] = tid;for (int j = 0; j < (i + 1) / 2; j++){int t = path[j];path[j] = path[i - j];path[i - j] = t;}FILE* fp = NULL;fopen_s(&fp, "e:\\path.txt", "w");if (fp == NULL)return -1;cout << "从起点" << srcVexId << "到终点" << dstVexId << "的最短路径长度=" << vertex[dstVexId].distance << endl;fprintf(fp, "起点%d到终点%d的最短路径: %d\n", srcVexId, dstVexId, vertex[dstVexId].distance);fprintf(fp, "最短路径一共经过%d个顶点(包括起点和终点):\n", i + 1);for (int j = 0; j <= i; j++){fprintf(fp, "vertex id:%d\n", path[j]);}if (fp != NULL)fclose(fp);return 0;
}

这篇关于C/C++利用迪杰斯特拉最短路径算法,求解千万结点道路网最短路径的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/295327

相关文章

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot路径映射配置的实现步骤

《SpringBoot路径映射配置的实现步骤》本文介绍了如何在SpringBoot项目中配置路径映射,使得除static目录外的资源可被访问,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一... 目录SpringBoot路径映射补:springboot 配置虚拟路径映射 @RequestMapp

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3