题目要求:将如下无向图使用邻接矩阵的方法进行保存,并进行深搜、广搜、建立最小生成树操作。
功能拓展:在实现要求的前提下,进行简单的名称替换、权值替换、找最短路径、删除城市、增加城市、增加航线、删除航线等操作。
模块代码:
1、定位输入城市模块:
int LocateVex(AMGraph G, VerTexType u[]) { //查询输入的城市是否在无向图中
for (int i = 0; i < G.vexnum; i++) //在图中循环将输入的城市与顶点一个个对比
if(strcmp(u,G.vexs[i])==0) return i; //如果输入的城市和第i个城市相同,返回i的值
printf("你输入的城市不在该图中\n"); //如果输入的城市和顶点城市都不相同,打印"你输入的城市不在该图中",并且返回-1
return -1;
}
2、建立无向图模块:
int CreateUDN(AMGraph& G) { //创建一个无向的航班图
printf("请输入总城市数和总航班数(例如:1 1):");
scanf("%d %d", &(G.vexnum),&(G.arcnum)); //将输入的值分别赋给图中边的个数和顶点个数
for (int i = 0; i < G.vexnum; i++) //利用for循环对图中的顶点进行赋值
{
printf("请输入第%d个城市的名称:",i+1);
scanf("%s", &(G.vexs[i]));
}
for (int i = 0; i < G.vexnum; i++) 将图中所有的边初始化为无穷大
for (int j = 0; j < G.vexnum; j++)
G.arcs[i][j] = MaxInt;
for (int k = 0; k < G.arcnum; k++) { //利用for循环和之前键入的边的个数对边进行赋值
printf("请输入你想输出航班价格的两个城市(例如:北京 哈尔滨):");
char v1[MVNum]; //定义两个字符数组存储输入的城市名称
char v2[MVNum];
scanf("%s %s", &v1,&v2); //将两个城市名称分别赋值给v1,v2数组
int i = LocateVex(G, &v1[0]);
int j = LocateVex(G, &v2[0]);
if(i==-1||j==-1){ //判断输入的城市是否不在图中,若不在图中则退出程序
printf("输入错误,该图中没有这个城市,程序退出执行\n\n\n");
exit(-1);
}
printf("请输入%s和%s两个城市之间的机票价格:",v1,v2);
int w; //定义一个整型变量来存储边的值
scanf("%d",&w); //将输入的值赋给w
G.arcs[i][j] = w; //由于该图是一个无向图,所以要对对称的位置都进行赋值
G.arcs[j][i] = G.arcs[i][j];
}
return OK;
}
3、展示创建的邻接矩阵
char ShowUDN(AMGraph &G){ //展示创建的无向图
printf(" "); //规范格式,使得输出看起来更加整齐
for(int i=0;i<G.vexnum;i++)
printf("%8s",G.vexs[i]); //先将各个按顺序输入的城市名称打印出来,方便对应观察邻接矩阵
printf("\n"); //规范格式,使得输出看起来更加整齐
for(int i=0;i<G.vexnum;i++){
printf("%8s",G.vexs[i]); //打印出规范格式的城市名称
{
for(int j=0;j<G.vexnum;j++)
printf("%8d",G.arcs[i][j]); //打印出规范格式的边的权值
printf("\n"); //换行,使得输出结果更加整齐
}
}
}
4、深搜模块
int sum;//用来计算机票价格
bool visited[MVNum]; //用来表示结点是否访问过
int DFS(AMGraph G, int v) //对建立的图进行深度优先搜索
{
visited[v]=TRUE; //将本次访问的v对应的位置置为1
printf("%s ",G.vexs[v]); //打印访问的顶点名字
for(int w=0;w<G.vexnum;w++)
{
if(G.arcs[v][w]!=MaxInt&&(!visited[w])){ //判断与访问顶点相邻的顶点是否被访问过,以及他们之间是否含有通路
sum=sum+G.arcs[v][w]; //计算航班总价格
DFS(G,w); //递归算法继续找之后的邻接顶点
}
}
return sum; //对航班总价进行返回
}
5、广搜模块:
由于广搜需要借助队列,因此将队列也一起放入广搜模块
typedef struct Queue{ //定义队列
int data[MVNum]; //队列大小
int head; //队头
int wei; //队尾
}Queue;
void InitQueue(Queue *q) //初始化队列
{
q->head= 0; //初始化队头、队尾
q->wei = 0;
}
int EmptyQueue(Queue *q) //判断队列是否为空
{
if(q->head == q->wei)
return 1;
else{
return 0;
}
}
void PushQueue(Queue *q,int t) //入队
{
if((q->wei+1)%MVNum == q->head) //说明队列已经满了
return;
else{
q->data[q->wei] = t;
q->wei = (q->wei +1)%MVNum; //队尾后移
}
}
void PopQueue(Queue *q,int *x) //出队
{
if(q->wei == q->head) //出队完毕
return;
else{
*x = q->data[q->head];
q->head = (q->head + 1)%MVNum; //队头后移
}
}
void BFS(AMGraph G) //通过输入的起点城市,对邻接矩阵进行广度优先的遍历
{
int i,j;
int k;
int sum=0;
Queue Q;
for(i=0;i<G.vexnum;i++)
{
visited[i] = 0; //初始化标记符
}
InitQueue(&Q); //队列初始化
for (i = 0; i < G.vexnum; i++)
{
if (!visited[i]) //判断以这个顶点为基准,有连接的其他顶点
{
visited[i] = 1; //标记遍历的这个顶点
printf("%s ",G.vexs[i]);
PushQueue(&Q, i); //入队
while (!EmptyQueue(&Q)) //队列中还有数据,说明这个顶点连接的其他顶点还没有遍历完
{
PopQueue(&Q,&i); //出队
for (j = 0; j < G.vexnum; j++)
{
//以这个顶点为基准,遍历其他连接的顶点
if (!visited[j] && G.arcs[i][j] != MaxInt)
{
sum=sum+G.arcs[i][j];
visited[j] = 1; //与之连接的顶点作上标记,便于后序顶点跳过相同的遍历
printf("%s ", G.vexs[j]); //输出与之相邻连接的顶点
PushQueue(&Q, j); //让与之连接的顶点其位置入队
}
}
}
}
}
printf("\n在不考虑返程情况下总航班费为:%d\n",sum);
}
6、建立最小生成树模块
void prim(AMGraph G,int v) //利用Prim算法,通过输入的起点城市来建立最小生成树
{
int con,row;
int min;
int parent[G.vexnum]={0}; //保存临接顶点下标的数组,所有顶点parent全部赋值为0,将0号顶点(以0号顶点作为第一个顶点)加入生成树
int dist[G.vexnum]; //记录当前生成树到剩余顶点的最小权值
for(row=1;row<G.vexnum;row++)
{
dist[row]=G.arcs[0][row]; //将与下标为0的顶点有边的权值存入dist数组
}
printf("\n");
//核心算法
for(con=1;con<G.vexnum;con++) //只需要循环n-1次,n为顶点的个数
{
min=MaxInt; //设置一个最大值来比较权值
int b,a;
//找出最小的最小权值min并将下标赋给b
for(a=1;a<G.vexnum;a++) //从1号顶点开始查找
{
if(dist[a]!=0&&dist[a]<min) //不在生成树中的顶点且权值最小的
{
min=dist[a]; //更新最小的权值
b=a; //更新找到新的顶点下标并赋值给b
} //a++,执行下一顶点直到a=Graph->Nv跳出循环
}
//Graph->Data[parent[b]]为Graph->Data[b]的父母顶点的信息
printf("(%s,%s,%d)\n",G.vexs[parent[b]],G.vexs[b],min); //打印信息
dist[b]=0; //将这个顶点加入生成树
//生成树加入了新的顶点从下标为1的顶点开始更新parent数组值
for(a=1;a<G.vexnum;a++)
{
if(dist[a]!=0&&G.arcs[b][a]<dist[a]) //如果新加入树的顶点b使得权值变小
{
dist[a]=G.arcs[b][a]; //更新最小的权值
parent[a]=b; //修改这条边邻接的顶点,也就是表示这条边是从选出的顶点b指过来的,方便打印
}
}
}
}
7、查找最短路径模块
void shortestway(AMGraph G) //根据输入的两个城市,找到一条机票价格最低的路径并打印
{ int i,j,k;
int a,b;
int d[MVNum][MVNum],path[MVNum][MVNum]; //用来存储最短路径权值和和最短路径的下标
char res1[MVNum],res2[MVNum]; //用来存放输入城市的名称
for(i=0;i<G.vexnum;i++) //对数组d和path进行初始化
{
for(j=0;j<G.vexnum;j++)
{
d[i][j]=G.arcs[i][j];
path[i][j]=j+1;
}
}
for(k=0;k<G.vexnum;k++) //拿出每个顶点作为遍历条件
{ //对于第k个顶点来说,遍历网中任意两个顶点,判断间接的距离是否更短
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
if(d[i][k]+d[k][j]<d[i][j]) //判断经过顶点k的距离是否更短,如果判断成立,则存储距离更短的路径
{
d[i][j]=d[i][k]+d[k][j];
path[i][j]=path[i][k];
}
}
}
}
printf("输入要查找价格的两个城市(例如:北京 成都):");
scanf("%s %s",&res1,&res2);
a=LocateVex(G,&res1[0])+1; //将输入城市分别在邻接矩阵中找到
b=LocateVex(G,&res2[0])+1;
printf("%s到达%s的最少价格为:%d元\n",res1,res2,d[a-1][b-1