数据结构课程设计(九)---公交线路提示

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

猜你喜欢

转载自blog.csdn.net/zhulinhao/article/details/106899136