数据结构课程设计(九)---公交线路提示
1、任务简述:
上网下载真实南京公交线路图,建立南京主要公交线路图的存储结构
要求:
(1)输入任意两站点,给出转车次数最少的乘车路线。
(2)输入任意两站点,给出经过站点最少的乘车路线。
(3)加分项:可以输出全部符合要求的乘车路线
2、算法描述:
站与站之间的图直接读取文件即可得到。而最小换乘需要一张车与车之间的图。在原本的站与站的图里,我在邻接表里存储了车的信息(可以知道每一条弧是哪一路车上的),可以方便后来构造车与车的图
最短路径算法:
未使用dijkstra算法而通过改进广度优先搜索来实现最短路径。
因为不论是站与站之间的图还是车与车之间的图,每一个结点之间权值都是1,将其联想到广度优先里一层一层搜索的思路。因为搜索完一层才会搜索下一层,所以最先搜索到终点的那一层肯定是最小层数(最少战点或最少换乘),肯定可以反向往上层查找找出一条最短路径。
实现方法是运用广度优先搜索里的visited数组,不用其表示是否访问过,而用其存储层数。广度搜索中队列每弹出一个站点,就搜索所有和该站点相邻的结点,如果未访问,就visited中置为1,表示访问过,然后进队列。这里改进为,visited置为当前层数+1。如果找到了终点,就结束广度搜索,并且用栈配合visited数组一层一层的往上找,最后将全部出栈,得出的就是一条最短路径。
图解:
3、源代码
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h> #define STACK_INIT_SIZE 100
#define STACKINCREMENT 10using namespace std;/*以下为队列的结构定义*/
template <class T>
struct QNode
{T data;QNode* next;
};template <class T>
class Queue
{public:QNode<T>* front; //队头指针(链表头) QNode<T>* rear; //队尾指针(链表尾) Queue(){front = rear = new QNode<T>;front->next = NULL;}~Queue(){QNode<T>* p, * q;p = q = front;while (p != NULL){p = p->next;delete q;q = p;}front = rear = NULL;}void ClearQueue(){QNode<T>* p, * q;p = q = front->next;while (p != NULL){p = p->next;delete q;q = p;}rear = front;}bool QueueEmpty(){if (front == rear){return true;}else{return false;}}int QueueLength(){int count = 0;QNode<T>* p;p = front->next;while (p != NULL){count++;p = p->next;}return count;}QNode<T>* GetHead(){if (front == rear){cout << "队空" << endl;return NULL;}else{return front->next;}}void EnQueue(T d){QNode<T>* p;p = new QNode<T>;p->data = d;p->next = NULL;rear->next = p;rear = rear->next;}T DeQueue(){if (front == rear){cout << "队空" << endl;}else{T ans;QNode<T>* p;p = front->next;ans = p->data;front->next = p->next;if (rear == p){rear = front;}delete p;return ans;}}};/*以下是栈的结构定义*/
template<class T>
class Stack
{private:T* data;T* base;T* top;int stacksize;
public:Stack(void){data = new T[STACK_INIT_SIZE];top = base = data;stacksize = STACK_INIT_SIZE;}void ClearStack(){top = base;}bool StackEmpty(){if (top == base){return true;}else{return false;}}int StackLength(){return top - base;}T GetTop(){return *(top - 1);}T* GetBaseLoca(){return base;}void Push(T num) //进栈 {if (top - base != stacksize){*(top) = num;top++;}else{int i;T* tmp;tmp = new T[stacksize + STACKINCREMENT];for (i = 0; i < stacksize; i++){tmp[i] = data[i];}delete[]data;data = tmp;stacksize += STACKINCREMENT;*(top) = num;top++;}}T Pop() //出栈 {if (top != base){top--;return *(top);}else{return '\0';}}void ShowStack(){int i;if (top - base == 0){cout << "空栈" << endl;}else{cout << "栈底到栈顶:";for (i = 0; i < top - base; i++){cout << data[i] << " ";}cout << endl;}}
};float min(float a, float b)
{if (a < b){return a;}else{return b;}
}struct ArcNode
{int adjvex; //该弧所指向的顶点的位置vertices[adjvex]int road; //表示这一条弧是第几路车,方便构建车与车之间的图ArcNode* nextarc; //指向下一条弧的指针
};struct VNode
{char name[50]; //站点名ArcNode* firstarc; //指向第一条依附于该顶点的弧的指针
};class ALGraph {private:VNode vertices[6000]; //顶点向量,每一站之间都有一个顶点int vexnum, arcnum; //图的当前顶点数和弧数int* visited; //用于深度优先搜索和广度优先搜索
public:void AddArcNode(int v1, int v2, int road)//为vertices[v1]和vertices[v2]两个站之间添加一条无向边,并且记录这一条无向边第road路上的{ArcNode* Ap;Ap = vertices[v1].firstarc;if (Ap == NULL){vertices[v1].firstarc = new ArcNode;vertices[v1].firstarc->adjvex = v2;vertices[v1].firstarc->road = road;vertices[v1].firstarc->nextarc = NULL;}else{while (Ap->nextarc != NULL){Ap = Ap->nextarc;}Ap->nextarc = new ArcNode;Ap = Ap->nextarc;Ap->adjvex = v2;Ap->road = road;Ap->nextarc = NULL;}Ap = vertices[v2].firstarc;if (Ap == NULL){vertices[v2].firstarc = new ArcNode;vertices[v2].firstarc->adjvex = v1;vertices[v2].firstarc->road = road;vertices[v2].firstarc->nextarc = NULL;}else{while (Ap->nextarc != NULL){Ap = Ap->nextarc;}Ap->nextarc = new ArcNode;Ap = Ap->nextarc;Ap->adjvex = v1;Ap->road = road;Ap->nextarc = NULL;}arcnum++;//边数++}ALGraph(){arcnum = 0;int i, j, count;int road;int pre_p;fstream rf;ArcNode* Ap;char str[3000];rf.open("南京公交线路.txt", ios::in);if (rf.fail()){cout << "南京公交线路.txt打开失败" << endl;exit(0);}vexnum = 0;road = 0;count = 0;while (!rf.eof()){rf >> road;if (rf.eof()){break;}rf.getline(str, 3000, '\n');i = 0;while (str[i] != ' ') i++;while (str[i] == ' ') i++;while (1){for (j = 0; str[i] != ',' && str[i] != '\0'; i++, j++){vertices[vexnum].name[j] = str[i];}vertices[vexnum].name[j] = '\0';for (j = 0; j < vexnum; j++){if (strcmp(vertices[j].name, vertices[vexnum].name) == 0){if (count > 0){AddArcNode(j, pre_p, road);}pre_p = j;count++;break;}}if (j == vexnum){vertices[vexnum].firstarc = NULL;if (count > 0){AddArcNode(pre_p, vexnum, road);}pre_p = vexnum;vexnum++;count++;}if (str[i] != '\0'){i++;}else{break;}}pre_p = 0;count = 0;}rf.close();visited = new int[vexnum];}~ALGraph(){ArcNode* Ap, * pre_Ap;for (int i = 0; i < vexnum; i++){Ap = vertices[i].firstarc;if (Ap == NULL);else{pre_Ap = Ap;while (1){Ap = Ap->nextarc;if (Ap == NULL){break;}delete pre_Ap;pre_Ap = Ap;}}}}ArcNode* AdjVex(int i){ArcNode* Ap;Ap = vertices[i].firstarc;if (Ap == NULL){return NULL;}else{return Ap;}}int GetVexNum(){return vexnum;}int FindRoad(char* n){for (int i = 0; i < vexnum; i++){if (strcmp(vertices[i].name, n) == 0){return i;}}return -1;}VNode* GetVNode(){return vertices;}bool IsAdj(int i, int j)//判断vertices[i]与vertices[j]是否相邻{ArcNode* Ap;Ap = vertices[i].firstarc;if (Ap == NULL);else{while (Ap != NULL){if (Ap->adjvex == j){return true;}Ap = Ap->nextarc;}}Ap = vertices[j].firstarc;if (Ap == NULL);else{while (Ap != NULL){if (Ap->adjvex == i){return true;}Ap = Ap->nextarc;}}return false;}void MiniRoad(char* start, char* end) //用广度优先搜索求最短路径//start是起始站点的名字,end的是结束站点的名字{for (int i = 0; i < vexnum; i++){visited[i] = 0;}int v, u;int s, e;ArcNode* w = NULL;int i, j;int flag = 0;Queue<int> Q;for (i = 0; i < vexnum; i++){if (strcmp(start, vertices[i].name) == 0){s = i;}if (strcmp(end, vertices[i].name) == 0){e = i;}}v = s;visited[v] = 1;Q.EnQueue(v);while (!Q.QueueEmpty())//广度优先搜索{if (flag == 1){break;}u = Q.DeQueue();for (w = vertices[u].firstarc; w != NULL; w = w->nextarc){if (visited[w->adjvex] == 0){visited[w->adjvex] = visited[u] + 1;//往深一层搜索if (w->adjvex == e){flag = 1;//已经走到了终点break;}Q.EnQueue(w->adjvex);}}}ArcNode* Ap;Stack<int> S_sta;Stack<int> S_road;S_sta.Push(w->adjvex);i = w->adjvex;cout << "经过站最少的路线为:" << endl;cout << "共:" << visited[w->adjvex] << "站" << endl;for (int deep = visited[w->adjvex] - 1; deep > 1; deep--)//i是层数{for (j = 0; j < vexnum; j++)//j为下标{if (visited[j] == deep && IsAdj(i, j))//如果是上一层并且两站点相邻{S_sta.Push(j);//j可以在路线上,进栈i = j;//i成为上一层break;//结束循环并且往更上一层寻找}}}cout << vertices[s].name;i = s;while (!S_sta.StackEmpty())//输出路线{cout << "——>";j = S_sta.Pop();Ap = vertices[j].firstarc;while (Ap->adjvex != i){Ap = Ap->nextarc;}if (S_road.StackEmpty() || (S_road.GetTop() != Ap->road))//于此同时记录路线上的站点的乘车路线,并记录{S_road.Push(Ap->road);}cout << vertices[j].name;i = j;}cout << endl << endl;cout << "乘车路线为:" << endl;cout << "共:" << S_road.StackLength() << "路车" << endl;cout << S_road.Pop() << "路";while (!S_road.StackEmpty()){cout << "——>";cout << S_road.Pop() << "路";}cout << endl << endl;}};struct Arc
{int adjvex; //该弧所指向的顶点的位置vertices[adjvex]Arc* nextarc; //指向下一条弧的指针
};struct BusNode
{Arc* firstarc; //指向第一条依附于该顶点的弧的指针
};class LGraph//这个类是车与车之间的图
{private:BusNode vertices[1000]; //顶点向量,从1开始int vexnum, arcnum; //图的当前顶点数和弧数int* visited;
public:void AddArc(int v1, int v2){int flag;Arc* Ap;Ap = vertices[v1].firstarc;if (Ap == NULL){vertices[v1].firstarc = new Arc;vertices[v1].firstarc->adjvex = v2;vertices[v1].firstarc->nextarc = NULL;}else{flag = 0;while (Ap->nextarc != NULL){if (Ap->adjvex == v2) flag = 1;//说明两辆车可以换乘的信息已经记录了Ap = Ap->nextarc;if (Ap->adjvex == v1) flag = 1;//说明两辆车可以换乘的信息已经记录了 }if (!flag){Ap->nextarc = new Arc;Ap = Ap->nextarc;Ap->adjvex = v2;Ap->nextarc = NULL;}}Ap = vertices[v2].firstarc;if (Ap == NULL){vertices[v2].firstarc = new Arc;vertices[v2].firstarc->adjvex = v1;vertices[v2].firstarc->nextarc = NULL;}else{flag = 0;while (Ap->nextarc != NULL){if (Ap->adjvex == v1) flag = 1;//说明两辆车可以换乘的信息已经记录了 Ap = Ap->nextarc;if (Ap->adjvex == v1) flag = 1;//说明两辆车可以换乘的信息已经记录了 }if (!flag){Ap->nextarc = new Arc;Ap = Ap->nextarc;Ap->adjvex = v1;Ap->nextarc = NULL;}}arcnum++;}LGraph(ALGraph* G)//用站与站之间的图来构建{vexnum = 0;ArcNode* Ap;Arc* Bp;int i, j, k;int count;int Bus[500];for (i = 0; i < G->GetVexNum(); i++){Ap = G->GetVNode()[i].firstarc;for (j = 0; j < 500; j++){Bus[j] = 0;}if (Ap == NULL){continue;}else{count = 0;while (Ap != NULL){for (k = 0; k < count; k++){if (Ap->road == Bus[k]){break;}}if (k == count){Bus[count++] = Ap->road;if (Ap->road > vexnum){vexnum = Ap->road;}}Ap = Ap->nextarc;}for (k = 0; k < count - 1; k++){for (j = k + 1; j < count; j++){if (Bus[j] != Bus[k]){AddArc(Bus[j], Bus[k]);}}}}}visited = new int[vexnum + 1];}~LGraph(){Arc* Ap, * pre_Ap;for (int i = 1; i < vexnum + 1; i++){Ap = vertices[i].firstarc;if (Ap == NULL);else{pre_Ap = Ap;while (1){Ap = Ap->nextarc;if (Ap == NULL){break;}delete pre_Ap;pre_Ap = Ap;}}}}bool IsAdj(int i, int j)//判断vertices[i]与vertices[j]是否相邻{Arc* Ap;Ap = vertices[i].firstarc;if (Ap == NULL);else{while (Ap != NULL){if (Ap->adjvex == j){return true;}Ap = Ap->nextarc;}}Ap = vertices[j].firstarc;if (Ap == NULL);else{while (Ap != NULL){if (Ap->adjvex == i){return true;}Ap = Ap->nextarc;}}return false;}int FindMinTime(int start, int end) //从v开始进行广度优先搜索{if (start == end){return 0;}for (int i = 1; i < vexnum + 1; i++){visited[i] = 0;}int v, u;Arc* w = NULL;int i, j;int flag = 0;Queue<int> Q;v = start;visited[v] = 1;Q.EnQueue(v);while (!Q.QueueEmpty()){if (flag == 1){break;}u = Q.DeQueue();for (w = vertices[u].firstarc; w != NULL; w = w->nextarc){if (visited[w->adjvex] == 0){visited[w->adjvex] = visited[u] + 1;if (w->adjvex == end){flag = 1;//已经走到了终点break;}Q.EnQueue(w->adjvex);}}}return visited[w->adjvex];}void PrintMinTransform(int start, int end){if (start == end){cout << "两站位于" << start << "路车路线上" << endl;return;}for (int i = 1; i < vexnum + 1; i++){visited[i] = 0;}int v, u;Arc* w = NULL;int i, j;int flag = 0;Queue<int> Q;v = start;visited[v] = 1;Q.EnQueue(v);while (!Q.QueueEmpty()){if (flag == 1){break;}u = Q.DeQueue();for (w = vertices[u].firstarc; w != NULL; w = w->nextarc){if (visited[w->adjvex] == 0){visited[w->adjvex] = visited[u] + 1;if (w->adjvex == end){flag = 1;//已经走到了终点break;}Q.EnQueue(w->adjvex);}}}Arc* Ap;Stack<int> S;S.Push(w->adjvex);i = w->adjvex;cout << "换成最少的路线为:" << endl;if (visited[w->adjvex] <= 2){cout << "不用换乘" << endl;}else{cout << "共换乘坐:" << visited[w->adjvex] - 1 << "次" << endl;}for (int deep = visited[w->adjvex] - 1; deep > 1; deep--)//i是层数{for (j = 0; j < vexnum; j++)//j为下标{if (visited[j] == deep && IsAdj(i, j)){S.Push(j);i = j;break;}}}cout << start;i = start;while (!S.StackEmpty()){cout << "——>";cout << S.Pop();}cout << endl << endl;}void MiniTransform(ALGraph* G, int start, int end) //对G中每一个点都进行广度优先搜索{int i, j;for (i = 1; i < vexnum + 1; i++){visited[i] = 0;}ArcNode* Ap;int s_road[100];int e_road[100];int s_count = 0;int e_count = 0;char s[50];char e[50];Ap = G->GetVNode()[start].firstarc;while (Ap != NULL){s_road[s_count++] = Ap->road;Ap = Ap->nextarc;}Ap = G->GetVNode()[end].firstarc;while (Ap != NULL){e_road[e_count++] = Ap->road;Ap = Ap->nextarc;}int min = 99999;int tmp = 0;int min_s = 0;int min_e = 0;for (i = 0; i < s_count; i++){for (j = 0; j < e_count; j++){tmp = FindMinTime(s_road[i], e_road[j]);if (tmp <= min){min = tmp;min_s = s_road[i];min_e = e_road[j];}}}PrintMinTransform(min_s, min_e);}
};int main()
{int start, end;char s[50];char e[50];cout << "请输入起点:";cin >> s;cout << "请输入终点:";cin >> e;ALGraph A;LGraph B(&A);start = A.FindRoad(s);end = A.FindRoad(e);if (start == -1 || end == -1){cout << "输入站点有误!" << endl;return 0;}
if(start==end)
{cout << "\n不需要乘坐公交车,起点,终点位置相同!" <<endl;return 0;
}A.MiniRoad(s, e);cout << endl << endl << "最少换乘" << endl;B.MiniTransform(&A, start, end);
}//997行
4、运行结果
原始数据:
输入非法数据:
输入相同的合法数据:
5、总结
心得体会:
运行结果正确,不过该题难度较大,相比dijkstra对每一个点每一次循环都对每个点进行更新,广度搜索每个点只访问一次。缺点是由于数据结构的问题,我如果要在最少换乘输出在哪一站换乘会相对麻烦。或许在从站与站的图到车与车的图转换时,可以把中转点记录到邻接表中。
遇到问题和解决方法:
如果你们仔细看我的代码,你们会发现我只用了宽搜,没有用深搜,因为如果一条一条的遍历过去,那么实在是他花时间了,所以,我想出了一个新的思路:
数据结构课程设计(九)---公交线路提示相关推荐
- 公交换乘系统c语言,数据结构课程设计报告(公交换乘).docx
课 程 设 计 报 告 题目: 武昌地区公交查询与换乘推荐 课程名称: 数据结构课程设计 专业班级: 学 号: 姓 名: 指导教师: 报告日期: 计算机科学与技术学院 任 务 书 设计内容 掌握图.查 ...
- 课程设计:公交线路管理系统
#include <iostream> #include <string> #include<iomanip> #include <fstream> u ...
- 算法与数据结构课程设计城市公交管理系统(C、C++)
城市公交管理系统 项目功能模块(需要源码请私信) 1.添加功能: [1]添加公交站,将站点信息写入stations.txt中: [2]添加交通路线,将路线信息写入bus.txt中: 2.删除功能: [ ...
- 数据结构课程设计 公交系统
大家好! 这是我的第一篇文章,是将这学期的数据结构课设报告整理出来的.可能还有些小错误,还请多多指正. 数据结构课程设计<公交系统> 一.引言 (一)课题描述 (二)设计要求 二.总体设计 ...
- 数据结构课程设计4:公交线路提示
1.任务: [问题描述] 上网下载真实南京公交线路图,建立南京主要公交线路图的存储结构. [基本要求] (1)输入任意两站点,给出转车次数最少的乘车路线. (2)输入任意两站点,给出经过站点最少的乘车 ...
- 基于数据结构和C语言实现公交管理系统(含文档和代码)数据结构课程设计
目录 第1章 课程设计内容及要求 第2章 需求分析 第3章 算法设计 3.1 设计思想 3.2 设计表示 第4章 系统调试及测试(含代码) 4.1 功能截图 4.2 实现代码 第5章 课程设计心得 5 ...
- C/C++数据结构课程设计安排
C/C++数据结构课程设计安排 数据结构课程设计安排 课程设计学时:32学时 课程设计目的:综合应用数据结构课程中所学的数据结构:线性表.栈.队列.数组.广义表.树.二叉树.图.查找表中的一种或多种数 ...
- 数据结构课程设计报告(附代码)
数据结构课程设计报告 一.实训目的 通过课程设计,学会运用数据结构知识,针对具体应用,自己设计合理数据结构,确定存储结构,并能设计具体操作算法,选择使用具体语言进行实现.掌握C++较复杂程序的组织和设 ...
- 2018数据结构课程设计报告
目录 一.引言 a) 编写目的 b) 项目背景 c) 术语说明 d) 参考资料 二.任务概述 a) 目标 b) 运行环境 c) 需求概述 d) 条件与限制 三. 总体设计 a) 处理流程 b) 总体结 ...
- 山东大学数据结构课程设计实验五(低风险出行系统)
数据结构课程设计(五)--低风险出行系统 前言 题目要点 ①生成数据 ②要给定两种最短路解法 ③创立文件 ④模拟时间流动并与用户交互 代码讲解 源代码 写在最后 前言 数据结构课程设计第五题是每一个同 ...
最新文章
- tomcat 7/8 启动非常慢的解决方法
- centos7+ docker1.12 实践部署docker及配置direct_lvm
- 计算机应用英语考什么,网考计算机应用基础(本)试卷10(国外英语资料).doc
- 如何解决连接不上us.archive.ubuntu.com
- OpenAPI实现云主机闪电交付最佳实践
- 海龟绘图两小时上手C语言 - 0 引言
- Cmake之基本语法
- 小白记事本--JAVA入门
- 字符串数组排序,如果可以保证前一个字符的末尾与后一个字符的开头相同,返回1,否则返回-1...
- 百度程序题目----连续数问题
- 【Web前端】一文带你吃透HTML(完整篇)
- mysql 创建外部表_Greenplum
- python tkinter 窗口颜色--数据和名称
- 【转载】R6034错误,C Runtime Error
- Dataset之CV:人工智能领域数据集集合(计算机视觉CV方向数据集)之常见的计算机视觉图像数据集大集合(包括表面缺陷检测数据集,持续更新)
- 电子产品进水后如何处理
- 芯片刀片服务器,超微SuperBlade系列刀片服务器产品介绍
- 计算机程序存储在哪里,计算机正在运行的程序存放在哪里?
- RecyclerView---高仿网易新闻客户端
- windows虚拟机安装打印机的方式