寻找出发地到目的地最省钱或最省时间的算法(迪杰斯特拉最短路径)(调用百度地图API展示最短路径)

该程序使用C++实现了Dijkstra最短路径算法,用于找出两个城市之间的最短路费和时间路径。同时,包含了深度优先遍历算法,能够从指定城市开始遍历整个城市网络。程序读取CSV文件获取城市和路线信息,并通过邻接矩阵存储图结构。此外,还提供了一个可视化功能,以HTML地图的形式展示最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VS2019

// MapRoutes.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include <vector>
#include<Windows.h>
#define INFINITY 99999 
using namespace std;
#define N  99999;

typedef struct {//cities的结构体
	bool pass = false;
	string country, city;
	float latitude = 0, longitude = 0;
	bool Visited = false;//用于深度遍历的辅助数组
	bool ver[199];//辅助
}City, Vertex;

typedef struct
{//routes的结构体
	string origin_city, destination_city;
	string transport;
	float time, cost;
	string other_info;
}Route;
typedef struct {
	Vertex* vertex;
	Route arc[199][199];//这个要是写成动态分配的就更好了,我这里写“死”了
	int vexnum, arcnum;
}Graph;

//顶点查找位置
int Locate(string city, City ct[])
{
	int i = 0;
	for (i = 0; i < 199; i++)
	{
		if (city == ct[i].city)
			return i;//如果相同,就返回位置

	}
	return -1;
}
void getCity(City* ct);//读取城市文件
void getRoutes(Route* route);//读取路线文件
void Create(Graph& g, City* ct, Route* route);//创建邻接矩阵
void DFS(int ori, City* ct);//深度遍历
void DFStraverse(City* ct);
void get_shortest_DJS_path(Graph& g, City* ct, int depart, int dest, float D[], int S[], int predecessor[]);//获得最短路费路径
void get_shortest_time_DJS_path(Graph& g, City* ct,int depart,int dest,float D[],int S[]);//获得最短时间路径
void Visualize(Graph& g, int depart, int dest, vector<int>a);//可视化函数

//主函数
int main()
{	
	City ct[201];
	Route  route[1975];

	//读取两个文件
	getCity(ct);
	getRoutes(route);

	//创建邻接矩阵
	Graph g;
	Create(g, ct, route);

	//深度优先遍历
	cout << "深度遍历结果:" << endl;
	DFStraverse(ct);//从ct[0]开始深度遍历
	cout << endl;

	//最短路径选择
	int choice, depart, dest;//depart  and dest 是出发点和目的地的代号
	cout << "最短路径:" << endl;
	cout << "1 for cost and 2 for time:";
	cin >> choice;
	string city1, city2;//起始点和目的地的城市名称
	float D[199] = { 0 };	//用D[i]表示当前所找到的从始点depart到每个目的地城市的最短路径的路费或者时间
	int S[199] = { 0 };	//已到达的最短路径的目的地的集合
	int predecessor[199];	//存放顶点的前驱,方便输出最短路

	//用户输入名称,由Locate函数确定每个城市位置
	cout << "请输入起始城市名称:";
	cin >> city1;
	depart = Locate(city1, ct);
	cout << "请输入目的地城市名称:";
	cin >> city2;
	dest = Locate(city2, ct);
	//判断是否在范围内,不在则结束程序
	if (depart > 199 || dest > 199 || depart < 0 || dest < 0)
	{
		cout << "找不到城市"<<endl;
		exit(0);
	}
	int t1 = clock();//计时开始,做出选择
	switch (choice)
	{
	case 1:
		get_shortest_DJS_path(g, ct,depart,dest,D,S,predecessor); break;//如果选择为1 ,则查找最短路费路径,并进行可视化(最短路径里面调用可视化函数)
	case 2:
		get_shortest_time_DJS_path(g, ct, depart, dest, D, S); break;//如果选择为2 ,则查找最短时间路径,并进行可视化
	default:
		break;

	}
	   int t2 = clock();
	   cout << "算法运行时间为:" << t2 - t1 << "ms" << endl;
}
//读取城市文件
void getCity(City* ct)
{
	//读取函数
	FILE* fp;
	fp = fopen(".\\cities.csv", "r");//读取文件
	if (!fp)
	{//报错
		cout << "ERROR!" << endl;
		exit(0);
	}
	char ch;
	int i = 0;
	float f;
	while (!feof(fp))
	{
		ch = fgetc(fp);
		for (; ch != ','; ch = fgetc(fp))
		{//读取逗号前面的字符,作为country
			ct[i].country += ch;
		}
		ch = fgetc(fp);
		for (; ch != ','; ch = fgetc(fp))
		{//同country
			ct[i].city += ch;
		}
		//输入经纬度
		fscanf_s(fp, "%f,", &f);
		ct[i].latitude = f;
		fscanf_s(fp, "%f\n", &f);
		ct[i].longitude = f;
		i++;
	}
	fclose(fp);//关闭文件
}
//读取路线
void getRoutes(Route* route)
{
	char ch;
	int i;
	FILE* fp1 = fopen(".\\routes.csv", "r");
	if (!fp1)
	{
		cout << "error";
		exit(0);
	}
	ch = fgetc(fp1);
	i = 0;
	while (!feof(fp1) && i < 1975)
	{//读取文件,同城市读取
		while (ch != ',') {
			route[i].origin_city += ch; ch = fgetc(fp1);
		}
		ch = fgetc(fp1);
		while (ch != ',')
		{
			route[i].destination_city += ch;
			ch = fgetc(fp1);
		}
		ch = fgetc(fp1);
		while (ch != ',')
		{
			route[i].transport += ch;
			ch = fgetc(fp1);
		}
		fscanf_s(fp1, "%f,", &route[i].time);
		fscanf_s(fp1, "%f,", &route[i].cost);
		ch = fgetc(fp1);
		while (ch != '\n')
		{
			route[i].other_info += ch; ch = fgetc(fp1);
		}
		ch = fgetc(fp1);
		i++;
	}
	fclose(fp1);
}
//邻接矩阵
void Create(Graph& g, City* ct,Route *route)
{
	//创建邻接矩阵
	int j = 0, i = 0;
	g.arcnum = 199 * 199;//边数
	g.vexnum = 199;//顶点数
	g.vertex = ct;
	for (i = 0; i < 199; i++)
		for (j = 0; j < 199; j++)
		{
			ct[i].ver[j] = false;//把第i个顶点到其他顶点状态初始化为0,未连通

			if (i == j)
			{//自己到自己时间和路径初始化为0
				g.arc[i][j].cost = 0;
				g.arc[i][j].time = 0;
			}
			else
			{//其他初始化为最大
				g.arc[i][j].cost = N;
				g.arc[i][j].time =N;
			}
		}
	for (i = 0; i < 1975; i++)
	{//对边赋权值
		int ori, de;
		ori = Locate(route[i].origin_city, ct);//定位
		de = Locate(route[i].destination_city, ct);
		if (route[i].cost < g.arc[ori][de].cost)
		{//判断,赋值
			g.arc[ori][de].cost = route[i].cost;//把路费赋值给边
			g.arc[ori][de].time = route[i].time;//把时间赋值给边
		}
		ct[ori].ver[de] =true;//把从第ori到第de顶点状态赋值为1,通路
	}

}

//从ori点开始深度遍历
void  DFS(int ori, City ct[])
{
	cout <<"("<<ori<<","<< ct[ori].city<<")" << "--> ";//输出遍历的城市
	ct[ori].Visited = true;//由于已经初始化为false,所以,遍历过的就变为true
	for (int j = 0; j < 199; j++)
	{
		if (ct[ori].ver[j] == true && ct[j].Visited == false)//括号内容说明;ori与jz之间连通且第j 个顶点未被遍历到
		{
			DFS(j, ct);//递归遍历

		}
		
	}
}
void DFStraverse(City* ct)
{
	int i;
	for (i = 0; i < 199; i++)
	{//从头到尾
		ct[i].Visited = false;//全部为false:未遍历状态
	}
	for (i = 0; i < 199; i++)
	{
		if (!ct[i].Visited)//若第i未被遍历的话
			DFS(i, ct);//调用DFS对第i 个顶点进行遍历输出
	}
}

//判断当前顶点是否在数组S中,(S数组就是已经被选择过的顶点的集合)
bool has_selected(int S[], int i, int k) {
	for (int j = 0; j < k; j++)
		if (i == S[j])
			return true;
	return false;
}
//从数组D中选取最小值(主函数中说明:数组D是一个辅助向量,D[i]表示当前所找到的从始点deaprt到每个终点dest的最短路径的时间或费用。)
int search_min(float D[], int S[], int k) {	//k为数组S中元素的个数,也就是说现在已经选了k条最短路径
	int min = -1;	//若为非连通图,则返回-1
	int m = INFINITY;
	for (int i = 0; i < 199; i++)
		if (!has_selected(S, i, k) && (D[i] < m)) {
			min = i;
			m = D[i];
		}

	return min;
}
void get_shortest_DJS_path(Graph& g, City* ct, int depart, int dest, float D[], int S[], int predecessor[])
{
	int min = 0, t = 0;	//初始化当前寻找到的最短路径的终点
	for (int i = 0; i < 199; i++)	//初始化所有顶点的前驱都为起始城市
		predecessor[i] = depart;

	for (int j = 0; j < 199; j++)
		D[j] = g.arc[depart][j].cost;//把费用赋值给D

	for (int i = 0; i < 199; i++) {
		for (int j = 0; j < 199; j++) {
			if (j == depart)	//防止寻找到的路径的起始点和终点相同
				continue;
			if (D[min] + g.arc[min][j].cost < D[j]) {
				D[j] = D[min] + g.arc[min][j].cost;//更新最短路径的费用
				predecessor[j] = min;	//	前驱的建立
			}
		}
		min = search_min(D, S, i);	//	寻找当前最短路径,返回最短路径的终点

		if (min == -1) {
			cout << "两个城市之间无交通方式\n";
			exit(0);
		}

		S[i] = min;		//编号为min的顶点作为最短路径的终点,所以把此顶点放入S中
		if (min == dest)	//如果现在选择的最短路径的终点恰好是dest,则停止寻找最短路径,减少时间复杂度
			break;

	}
	//输出最短路径的信息
	vector<int>order;//存储倒序城市的数组
	vector<int>vec_city;//正序输出数组
	int pre = dest;		
	cout << "From " << g.vertex[dest].city << " to " << g.vertex[depart].city << endl;
	cout << "路费为$" << D[dest] << endl;
	cout << "最短路径为:";
	order.push_back(Locate(g.vertex[dest].city, ct));//把目的地城市压入数组
	for (int i = 0; pre != depart; i++) {//把城市倒序输入
		pre = predecessor[pre];//找到前驱的代号,直到前驱为depart
		order.push_back(Locate(g.vertex[pre].city, ct));//每个城市放入此数组,且是倒序的
	}
	for (int i =order.size()-1; i >0; i--)
	{//正序输出
		cout << g.vertex[order[i]].city << "- >";
		vec_city.push_back(order[i]);
	}
	vec_city.push_back(order[0]);//放入起点城市
	cout << g.vertex[order[0]].city <<endl;
	int t0 = clock();
	Visualize(g, depart, dest, vec_city);//调用可视化函数
	int tt = clock();
	cout << "算法运行时间为:" << tt - t0<< "ms" << endl;

}

//类似于路费的函数,把cost变成了time输出即可
void get_shortest_time_DJS_path(Graph& g, City* ct,int depart,int dest,float D[],int S[])
{
	int min = 0;	//当前寻找到的最短路径的终点
	int predecessor[199];	//存放顶点的前驱,方便输出最短路
	vector<int>order;
	vector<int>vec_city;
	for (int i = 0; i < 199; i++)	//初始化所有顶点的前驱都为起始城市
		predecessor[i] = depart;

	for (int j = 0; j < 199; j++)
		D[j] = g.arc[depart][j].time;

	for (int i = 0; i < 199; i++) {
		for (int j = 0; j < 199; j++) {
			if (j == depart)	//防止寻找到的路径的起始点和终点相同
				continue;
			if (D[min] + g.arc[min][j].time < D[j]) {
				D[j] = D[min] + g.arc[min][j].time;
				predecessor[j] = min;	//	前驱的建立
			}
		}
		min = search_min(D, S, i);	//	寻找当前最短路径,返回最短路径的终点

		if (min == -1) {
			cout << "两个城市之间无交通方式\n";
			exit(0);
		}

		S[i] = min;		//编号为min的顶点作为最短路径的终点,所以把此顶点放入S中
		if (min == dest)	//如果现在选择的最短路径的终点恰好是dest,则停止寻找最短路径,减少时间复杂度
			break;

	}
	//输出最短路径的信息
	int pre = dest;		
	cout << "From " << g.vertex[dest].city << " to " << g.vertex[depart].city << endl;
	cout << "时间为:" << D[dest] << "h"<<endl;
	cout << "最短路径为:";
	order.push_back(Locate(g.vertex[dest].city, ct));
	for (int i = 0; pre != depart; i++) {//dest!=depart
		pre = predecessor[pre];
		order.push_back(Locate(g.vertex[pre].city, ct));
	}
	for (int i = order.size() - 1; i > 0; i--)
	{
		cout << g.vertex[order[i]].city << "- >";
		vec_city.push_back(order[i]);
	}
	vec_city.push_back(order[0]);
	cout << g.vertex[order[0]].city << endl;
	Visualize(g, depart, dest, vec_city);
}
//可视化函数(复制API代码,进行修改)
void Visualize(Graph& g, int depart, int dest,vector<int>vec_city)
{
	FILE* fp3;//打开文件
	fp3 = fopen(".\\MyGraph.html", "w");
	if (fp3 == NULL)
	{//报错
		cout << "error3";
		exit(0);
	}
	int j = 0;
	//s为API的代码
	string  s = "<!DOCTYPE html>\n\
		<html>\t\
		<head>\n\
		<meta http-equiv = 'Content-Type' content = 'text/html; charset=utf-8' />\n\
		<meta name = 'viewport' content = 'initial-scale=1.0, user-scalable=no' />\n\
		<style type = 'text/css'>\
		body, html, #allmap{ width: 100%; height: 100%; overflow: hidden; margin : 0; font-family:'wei ruan ya hei'; }\n\
	#l-map{ height:100%; width:78%; float:left; border-right:2px solid #bcbcbc; }\n\
	#r-result{ height:100%; width:20%; float:left; }\
		</style>\n\
		<script type = 'text/javascript' src = 'http://api.map.baidu.com/api?v=2.0&ak=nSxiPohfziUaCuONe4ViUP2N'></script>\n";
	//修改了Title:"从起始到目的地的最短距离"
	fprintf(fp3, "%s<title>Shortest path from %s to %s</title>\
</head><body><div id='allmap'></div></body>\
</html>\n\
<script type = 'text/javascript'>\n\
var map = new BMap.Map('allmap');\n\
var point = new BMap.Point(0,0);\n\
map.centerAndZoom(point, 2);\n\
map.enableScrollWheelZoom(true);\n", s.c_str(), g.vertex[depart].city.c_str(), g.vertex[dest].city.c_str());

	for (j = 0; j < vec_city.size(); j++)//调用上面的存储正序城市的数组
	{
		int m;
		m = vec_city[j];//m赋值为从depart到dest途经的城市的代码(顺序)
		//加入经过的城市的顶点的经纬度,所属国家及城市名称
		fprintf(fp3, "var marker%d =new BMap.Marker (new BMap.Point(%.4f,%.4f));\n\
map.addOverlay(marker%d);\n\
var infoWindow%d = new BMap.InfoWindow(\"<p style = 'font-size:14px;'>country%s<br/>city%s</p>\");\n\
marker%d.addEventListener(\"click\", function(){this.openInfoWindow(infoWindow%d);});\n\n", j, g.vertex[m].longitude, g.vertex[m].latitude, j, j, g.vertex[m].country.c_str(), g.vertex[m].city.c_str(), j, j);

		if (j < vec_city.size() - 1)
		{//判断,在到达最后一座城市之前,都有path,最后一座城市不执行这步
			int n;
			int t = j+1 ;//如果为J则不划线
			n = vec_city[t];

			fprintf(fp3, "var marker%d =new BMap.Marker (new BMap.Point(%.4f,%.4f));\n\
map.addOverlay(marker%d);\n\
var infoWindow%d = new BMap.InfoWindow(\"<p style = 'font-size:14px;'>country%s<br/>city%s</p>\");\n\
marker%d.addEventListener(\"click\", function(){this.openInfoWindow(infoWindow%d);});\n\n", t, g.vertex[n].longitude, g.vertex[n].latitude, t, t, g.vertex[n].country.c_str(), g.vertex[n].city.c_str(), t, t);
			//画路径,里面“red”可以更换颜色
			fprintf(fp3, "var path%d=new BMap.Polyline([\n\
                           new BMap.Point(%.4f,%.4f),\n\
                           new BMap.Point(%.4f,%.4f)],\n\
{ strokeColor:'red', strokeWeight : 2, strokeOpacity : 1.5 });\n\
 map.addOverlay(path%d);\n\n", j, g.vertex[m].longitude, g.vertex[m].latitude, g.vertex[n].longitude, g.vertex[n].latitude, j);
		}
	}
	fprintf(fp3, "</script>");
	fclose(fp3);
	ShellExecuteA(NULL, "open", ".\\MyGraph.html", NULL, NULL, 5);//弹出网页
}

结果:
在这里插入图片描述

问题解决:堆栈溢出:
1.项目–>属性–>链接器—>系统----->保留堆栈大小:更改数字从而增大堆栈大小---->确定,这样就可以运行了!(当然,也可以用其他更好的办法)。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值