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

相关文章

MySQL9.0默认路径安装下重置root密码

《MySQL9.0默认路径安装下重置root密码》本文主要介绍了MySQL9.0默认路径安装下重置root密码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录问题描述环境描述解决方法正常模式下修改密码报错原因问题描述mysqlChina编程采用默认安装路径,

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解