设计思路
设计一个校园交通查询导引系统,能让游客查询学校的全部地点,能够给出各个地点离
当前地点的距离,能够给出从一个地点到另外一个地点的最近距离和路径。同时,该软件还
具有常用的数据维护功能,例如调研地图信息文件、保存地图、地点添加、道路添加、道路
修改等功能。
该软件(操作都在终端页面)用到数据结构课程的图、排序、字符串等章节内容。
这里需要使用到Dijkstra算法求得最短路径
狄克斯特拉
(Dijkstra)
算法
基本思想是:设 G=(V,E)
是一个带权有向图
,
把图中顶点集合
V
分成两组: 第一组为已求出最短路径的顶点集合(
用
S
表示
,
初始时
S
中只有一个源点
,
以后每求得一 条最短路径 v0,
…
vk,
就将
vk
加入到集合
S
中
,
直到全部顶点都加入到
S
中
,
算法就结束了
) 第二组为其余未确定最短路径的顶点集合(
用
U
表示
)
。
按最短路径长度的递增次序依次把第二组的顶点加入 S
中。在加入的过程中
,
总保持从源点 v0 到
S
中各顶点的最短路径长度不大于从源点
v0
到
U
中任何顶点的最短路径长度。 此外,
每个顶点对应一个距离
,S
中的顶点的距离就是从
v0
到此顶点的最短路径长度
,U
中的顶点的距离从 v0
到此顶点只包括
S
中的顶点为中间顶点的当前最短路径长度。
狄克斯特拉算法的具体步骤如下:
(1)
初始时
,S
只包含源点
,
即
S={v0},v0
的距离为
0
。
U
包含除
v0
外的其他顶点
,U
中顶点 u 距离为边上的权
(
若
v0
与
u
有边
<v0,u>)
或∞。
(2)
从
U
中选取一个距离
v0
最小的顶点
v,
把
v
加入
S
中
(
该选定的距离就是
v0
到
v
的最 短路径长度)
。
(3)
以
v
为新考虑的中间点
,
修改
U
中各顶点的距离:若从源点
v0
到顶点
w(w
∈
U)
的距 离(
经过顶点
v)
比原来距离
(
不经过顶点
v)
短
,
则修改顶点
w
的距离值
,
修改后的距离值为顶点 v 的距离加上边
<v,w>
上的权。
(4)
重复步骤
(2)
和
(3)
直到所有顶点都包含在
S
中。
Dijkstra算法图示如下:
地图数据文件的结构定义
地图的信息需要保存到一个数据文件中,下图为一个数据文件的例子,在这个例子中,第一
行的
7
代表有
7
个地点,接下来的
7
行为
7
个地点的名称。接下来的
12
代表这个图共有
12 条边,最后的 12
行分别代表这
12
条边,每行的各个数据分别代表起点名称、终点名称、边的长度,边(
道路
)
名称和边
(
道路
)
与
x
轴的夹角。这个数据文件可以通过程序的“保存地图” 菜单得到,也可以用记事本编辑,然后通过程序的“调用地图信息文件”菜单读取到程序中。
在想要读取的路径下创建.txt文件
主要功能:
显示全部景点名称、路程导引、距离最近的景点、地图信息的变更、保存地图、调用地图文件、退出系统。
这里就不展示功能了,直接上代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "iostream"
using namespace std;
#define maxnum 100//最大顶点个数
#define Yes 1
#define No 0
#define INFINITY 999999
int visited[maxnum];
typedef char VertexName[20];//给char[20]数组起别名为Vertex
typedef struct
{
int adj;//相邻与否,或权值大小
char name[40];
int direction;
}Arc;
typedef struct
{
VertexName vexs[maxnum];
Arc arcs[maxnum][maxnum];
int vexnum,arcnum;
}LGragh;
//求顶点位置
int LocateVex(LGragh *M,VertexName v)
{
int k;
for(k=0;k< M->vexnum;k++)
{
if(!strcmp(M->vexs[k],v)) //strcmp字符串相等返回零
return k; //返回下标
}
printf("不存在景点%s\n",v);
return -1;//没有这个顶点
}
//加载
void load(LGragh *M)
{
FILE *fp;
int i,j,k;
int weight;
VertexName v1,v2;
fp=fopen("c:\\aaaa\\graph.txt","r");
//得到点的个数 7
fscanf(fp,"%d",&M->vexnum);
//初始化矩阵
for(i=0;i<maxnum;i++)
for(j=0;j<maxnum;j++)
M->arcs[i][j].adj=INFINITY;
//得到顶点
for(i=0;i<M->vexnum;i++) {
fscanf(fp,"%s",M->vexs[i]);
}
//得到弧数 12
fscanf(fp,"%d",&M->arcnum);
fgets(v1,4,fp);
//初始化弧
for(k=0;k<M->arcnum;k++)
{
fscanf(fp,"%s",v1);//从文件中读取
fscanf(fp,"%s",v2);
i=LocateVex(M,v1);//查找所在矩阵中的位置
j=LocateVex(M,v2);
fscanf(fp,"%d",&weight);
fscanf(fp,"%s",&M->arcs[i][j].name);
fscanf(fp,"%d",&M->arcs[i][j].direction);
if(i<0||j<0)
{
printf("??");
return;
}
M->arcs[i][j].adj=weight;
M->arcs[j][i].adj=M->arcs[i][j].adj;//无向的
strcpy(M->arcs[j][i].name,M->arcs[i][j].name);
M->arcs[j][i].direction=(M->arcs[i][j].direction+180)%360;
}
fclose(fp);
return;
}
void Printv(LGragh M)
{
int i;
for(i=0;i<M.vexnum;i++)
{
cout<<M.vexs[i]<<" ";//循环输出顶点名称,展示所有的景点
}
}
//V0初始点,p指针,D存储距离
void Dijkstra(LGragh G, int v0,int P[], int D[])
{
int v;
int final[maxnum];
int min;
//初始化
for(int v=0;v<G.vexnum;v++){
final[v]=0;
if(G.arcs[v0][v].adj!=0)
D[v]=G.arcs[v0][v].adj;//存储最短路径
else
D[v]=INFINITY;
if(D[v]<INFINITY)
P[v]=v0;//存储前驱结点
else
P[v]=-1;
}
D[v0]=0;
final[v0]=1;
for(int i=1;i<G.vexnum;i++)
{
min=INFINITY;
for(int w=0;w<G.vexnum;w++)
if(!final[w] && D[w]<min)
{
v=w;
min=D[w];
}
final[v]=1;
for(int w=0;w<G.vexnum;w++)
{
if(!final[w] && (min+G.arcs[v][w].adj<D[w]))
{
D[w]=min+G.arcs[v][w].adj;
P[w]=v;
}
}
}
}
void findRoad(char starts[],char ends[],LGragh *M)
{
int p[30],d[30];//路径和距离
int path[50];//存储路径顶点
int k=0,i;//路径景点计数,循环变量
int dir;//存储方向度数变量
int temp;
int start,end;//声明起点和终点
start=LocateVex(M,starts);//初始化起点和终点位置
end = LocateVex(M,ends);
if(start==-1||end==-1)//不存在
return;
Dijkstra(*M,start,p,d);//求得最短路径
printf("%s---%s 距离为%d\n",M->vexs[start],M->vexs[end],d[end]);
path[k++]=end;//从终点到起点的遍历路径
while(p[end]!=-1)//判断是否有前驱结点
{
path[k++]=p[end];//前驱结点添加到路径中
end=p[end];//更新end为前驱结点的位置
}
printf("\n");
for(i=k-1;i>0;i--)
{
if(i!=k-1)
{
temp=M->arcs[path[i]][path[i-1]].direction-dir;//当前边和前一条边的差值
if(temp<0)
temp=360+temp;
if(temp<45||temp>315)
printf("向前");
else if(temp>45 && temp<180)
printf("向左");
else
printf("向右");
}
//%s--- %s--- %s
printf("%s---",M->vexs[path[i]]);// 当前顶点
printf("%s---",M->arcs[path[i]][path[i-1]].name);//边名
dir = M->arcs[path[i]][path[i-1]].direction;//更新dir为前边,便于下次计算
printf("%s\n",M->vexs[path[i-1]]);//结束位置的顶点
}
}
void findPoint(char starts[],LGragh *M)
{
int start;
int p[30],d[30],i,j,min,k;
int dir;
int temp;
start=LocateVex(M,starts);
if(start==-1)
return;
Dijkstra(*M,start,p,d);
for(k=0;k<M->vexnum-1;k++)
{
d[start]=INFINITY;
min=d[0];j=0;
for(i = 0; i < M->vexnum; i++)
{
if(d[i]<min)
{
min=d[i];
j=i;
}
}
printf("\n %s 与当点位置的距离为 :%d",M->vexs[j],d[j]);
d[j]=INFINITY;
}
}
//添加一个顶点
void InsertVex(LGragh *G,VertexName v)
{
int i,j;
G->vexnum++;
strcpy(G->vexs[G->vexnum-1],v);
i=LocateVex(G,v);
for(j=0;j< G->vexnum;j++)
{
G->arcs[i][j].adj=INFINITY;//添加的顶点默认和其他顶点不邻接
G->arcs[j][i].adj=INFINITY;
}
}
//删除一个顶点
int DeVex(LGragh *G,VertexName v)
{
int i,j;
i=LocateVex(G,v);
if(i==-1)
{
printf("点%d不在该图中\n",v);
return No;
}
for(j=0;j< G->vexnum;j++)
{
G->arcs[i][j].adj=0;
G->arcs[j][i].adj=0;//删除与该顶点连接的弧
}
for(j=i;j<G->vexnum;j++)
strcpy(G->vexs[j],G->vexs[j+1]); //空处的位置前移
G->vexnum--;
printf("删除顶点%s成功!\n",v);
return Yes;
}
//添加一条弧
int InserArc(LGragh *G)
{
VertexName v1,v2;
char name[40];
int weight;
int dir;
int i,j;
cout<<"请输入起点名称:"<<endl;
cin>>v1;
cout<<"请输入终点名称:"<<endl;
cin>>v2;
cout<<"请输入道路名称:"<<endl;
cin>>name;
cout<<"请输入距离:"<<endl;
cin>>weight;
cout<<"请输入道路方向:"<<endl;
cin>>dir;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(i==-1||j==-1)
{
printf("景点不在图中\n");
return No;
}
G->arcs[i][j].adj=G->arcs[j][i].adj=weight;
G->arcs[i][j].direction=dir;
strcpy(G->arcs[i][j].name,name);
return Yes;
}
//修改一条弧
int ChargeArc(LGragh *G)
{
VertexName v1,v2;
char name[40];
int weight;
int dir;
int i,j;
cout<<"请输入起点名称:"<<endl;
cin>>v1;
cout<<"请输入终点名称:"<<endl;
cin>>v2;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(i==-1||j==-1)
{
printf("景点不在图中\n");
return No;
}
cout<<"原来的距离,道路名,方向"<<endl;
cout<<G->arcs[i][j].adj <<" "<<G->arcs[i][j].name<<" "<<G->arcs[i][j].direction<<endl;
cout<<"请输入新道路名称:"<<endl;
cin>>name;
cout<<"请输入新距离:"<<endl;
cin>>weight;
cout<<"请输入新道路方向:"<<endl;
cin>>dir;
G->arcs[i][j].adj=G->arcs[j][i].adj=weight;
G->arcs[i][j].direction=dir;
G->arcs[j][i].direction=(dir+180)%360;
strcpy(G->arcs[i][j].name,name);
strcpy(G->arcs[j][i].name,name);
return Yes;
}
//删除一条弧
int DeArc(LGragh *G)
{
int i,j;
VertexName v1,v2;
cout<<"请输入起点名称:"<<endl;
cin>>v1;
cout<<"请输入终点名称:"<<endl;
cin>>v2;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
printf("删除成功!\n");
if(i==-1||j==-1)
{
printf("顶点不在该图中\n");
return No;
}
G->arcs[i][j].adj=G->arcs[j][i].adj=INFINITY;
return Yes;
}
//保 存 地 图
void saveMap(LGragh *M)
{
int i=0,j;
FILE *fp = fopen("d:\\a.txt","w");
//向文件写数据
fprintf(fp,"%d\n",M->vexnum);
for(i=0;i< M->vexnum;i++)
{
fprintf(fp,"\n");
fputs(M->vexs[i],fp);
}
fprintf(fp,"\n");
fprintf(fp,"%d",M->arcnum);
for(i=0;i<M->vexnum;i++)
{
for(j=i+1;j<M->vexnum;j++)
{
if(M->arcs[i][j].adj!=INFINITY)
{
fprintf(fp,"\n");
fputs(M->vexs[i],fp);
fprintf(fp,"\n");
fputs(M->vexs[j],fp);
fprintf(fp,"\n");
fprintf(fp,"%d",M->arcs[i][j].adj);
fprintf(fp,"\n");
fputs(M->arcs[i][j].name,fp);
fprintf(fp,"\n");
fprintf(fp,"%d",M->arcs[i][j].direction);
}
}
}
printf("保存到了d:a.txt文件下");
fclose(fp);//关闭文件没写
return;
}
//调 用 地 图 文 件
void loadN(LGragh *M,char f[20])//参数为结构体和路径长度
{
FILE *fp;
int i,j,k;
int weight;
VertexName v1,v2;
fp=fopen(f,"r");//对f路径下进行读取
//初始化顶点个数
fscanf(fp,"%d",&M->vexnum);//读取fp下的整数到M->vexnum里
//初始化矩阵的权值大小
for(i=0;i<maxnum;i++)
for(j=0;j<maxnum;j++)
M->arcs[i][j].adj=INFINITY;
//初始化顶点
for(i=0;i< M->vexnum;i++)
fscanf(fp,"%s",M->vexs[i]);
//初始化弧的个数
fscanf(fp,"%d",&M->arcnum);
fgets(v1,4,fp);
//初始化弧
for(k=0;k< M->arcnum;k++){
fscanf(fp,"%s",v1);//从文件中读取两个顶点的名称 例如v0和v1
fscanf(fp,"%s",v2);
i=LocateVex(M,v1);//查找所在矩阵中的位置
j=LocateVex(M,v2);
fscanf(fp,"%d",&weight);
fscanf(fp,"%s",M->arcs[i][j].name);
fscanf(fp,"%d",&M->arcs[i][j].direction);
if(i<0||j<0)
{
return;
}
M->arcs[i][j].adj=weight;
M->arcs[j][i].adj=M->arcs[i][j].adj;
strcpy(M->arcs[j][i].name,M->arcs[i][j].name);
M->arcs[j][i].direction=(M->arcs[i][j].direction+180)%360;
}
fclose(fp);
printf("加载成功!\n");
return;
}
///主函数/
int welcome();
void change(LGragh *M);
int main()
{
LGragh M;
int n;
char start[20],end[20];
M.vexnum=M.arcnum=0;
load(&M);
while(n!=7)
{
n=welcome();
switch(n){
case 1: Printv(M);
break;
case 2:
cout<<"请输入起点";
cin>>start;
cout<<"请输入终点";
cin>>end;
getchar();
findRoad(start,end,&M);
break;
case 3:
Printv(M);
cout<<"请输入您所在景点";
cin>>start;
getchar();
findPoint(start,&M);
break;
case 4:
change(&M);
break;
case 5:
saveMap(&M);
break;
case 6:
cout<<"请输入完成文件名称(包括盘符与路径)";
cin>>start;
loadN(&M,start);
break;
case 7:
break;
}
getchar();
}
}
int welcome()
{
int n;
cout<<" 旅 游 景 点 引 导 系 统 "<<endl;
cout<<" "<<endl;
cout<<" ############################################################"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 1. 显 示 全 部 景 点 名 称 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 2. 路 程 导 引 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 3. 离 我 最 近 的 景 点 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 4. 地 图 信 息 变 更 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 5. 保 存 地 图 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 6. 调 用 地 图 文 件 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ## 7. 退 出 系 统 ##"<<endl;
cout<<" ## ##"<<endl;
cout<<" ############################################################"<<endl;
cout<<"请输入:";
cin>>n;
getchar();
return n;
}
void change(LGragh *M)
{
int n,k;
char v1[20],v2[20];
cout<<"1. 增加景点"<<endl;
cout<<"2. 修改景点名称"<<endl;
cout<<"3. 删除景点"<<endl;
cout<<"4. 增加道路"<<endl;
cout<<"5. 修改道路"<<endl;
cout<<"6. 删除道路"<<endl;
cout<<"请输入:";
cin>>n;getchar();
switch(n)
{
case 1:
cout<<"请输入景点名称:"<<endl;
cin>>v1;
InsertVex(M,v1);
cout<<"添加景点成功!"<<endl; break;
case 2:
cout<<"请输入景点名称:"<<endl;
cin>>v1;
cout<<"请输入景点新名称:"<<endl;
cin>>v2;
k=LocateVex(M,v1);
strcpy(M->vexs[k],v2); break;
case 3:
cout<<"请输入景点名称:"<<endl;
cin>>v1;
DeVex(M,v1); break;
case 4:
InserArc(M);break;
case 5:
ChargeArc(M);break;
case 6:
DeArc(M);break;
}
}