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 10
using 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对每一个点每一次循环都对每个点进行更新,广度搜索每个点只访问一次。缺点是由于数据结构的问题,我如果要在最少换乘输出在哪一站换乘会相对麻烦。或许在从站与站的图到车与车的图转换时,可以把中转点记录到邻接表中。
遇到问题和解决方法:
如果你们仔细看我的代码,你们会发现我只用了宽搜,没有用深搜,因为如果一条一条的遍历过去,那么实在是他花时间了,所以,我想出了一个新的思路: