实验四 图的基本操作及应用
一、实验目的
1、使学生可以巩固所学的有关图的基本知识。
2、熟练掌握图的存储结构。
3、熟练掌握图的两种遍历算法。
4、掌握如何应用图解决各种实际问题。
二、实验内容
本次实验需完成两个题目,题目一必做,题目二或三选择其中一道。
三、实验准备知识
CreatGraph(&G, V, VR);// 按定义(V, VR) 构造图
DestroyGraph(&G);// 销毁图
LocateVex(G, u); // 若G中存在顶点u,则返回该顶点在图中“位置”;否则返回其它信息。
GetVex(G, v); // 返回 v 的值
PutVex(&G, v, value);// 对 v 赋值value。
FirstAdjVex(G, v); // 返回 v 的“第一个邻接点” 。若该顶点在 G 中没有邻接点,则返回“空”
NextAdjVex(G, v, w); // 返回 v 的(相对于 w 的) “下一个邻接点”。若 w 是 v 的最后一个邻接点,则返回“空”。
InsertVex(&G, v); //在图G中增添新顶点v。
DeleteVex(&G, v);// 删除G中顶点v及其相关的弧
DFSTraverse(G, v, Visit()); //从顶点v起深度优先遍历图G,并对每个顶点调用函数Visit一次且仅一次。
BFSTraverse(G, v, Visit()); //从顶点v起广度优先遍历图G,并对每个顶点调用函数Visit一次且仅一次。
四、实验内容
题目一: 图的遍历
[问题描述]
对给定图,实现图的深度优先遍历和广度优先遍历。
[基本要求]
以邻接表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列。
【测试数据】
由学生依据软件工程的测试技术自己确定。
[源代码](加注释)
#include<stdio.h>
#include<malloc.h>
#include <stdlib.h>
#include<iostream>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef int Boolean;
typedef char TElemType;
#define MVNum 100 //最大顶点数
typedef char VerTexType; //顶点类型
typedef int Bool;
bool visited[MVNum];
typedef char OtherInfo;
typedef struct ArcNode //边结点
{
int adjvex; //该边所指向的顶点的位置
struct ArcNode *nextarc; //指向下一条边的指针
OtherInfo info; //和边相关的信息
}ArcNode;
typedef struct VNode //顶点信息
{
VerTexType data;
ArcNode *firstarc; //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct //邻接表
{
AdjList vertices;
int vexnum,arcnum; //图的当前顶点数和边数
}ALGraph;
typedef struct QNode
{
int data;
struct QNode *next;
} QNode,*QueuePtr;
typedef struct
{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
int InitQueue (LinkQueue &Q)//构造一个空队列
{
Q.front=Q.rear=new QNode; //生成新结点作为头结点,队头和队尾指针指向此结点
Q.front->next==NULL; //头结点的指针域置空
return OK;
}
int EnQueue (LinkQueue &Q,int e)//插入元素为e为Q的新的队尾元素
{
QueuePtr p=new QNode;
p->data=e;
Q.rear->next=p;
Q.rear=p;
p->next=NULL;
return OK;
}
int DeQueue(LinkQueue &Q,int &e)//删除Q的队头元素,用e返回其值
{
if(Q.front==Q.rear)
return -1;
if(Q.front->next==Q.rear)
{
e=Q.front->next->data;
free(Q.front->next);
Q.front->next=NULL;
Q.rear=Q.front;
}
else{
QNode *p=Q.front->next;
e=p->data;
Q.front->next=p->next;
}
return 0;
}
int LocateVex(ALGraph &G,VerTexType u)
{//存在则返回u在顶点表中的下标;否则返回-1
int i;
for(i=0;i<G.vexnum;++i)
if(u==G.vertices[i].data)
return i;
//return -1;
}
Status CreateUDG(ALGraph &G) //采用邻接表表示法,创建无向图G
{
printf("请输入顶点和边数:");//输入总顶点数,总边数
cin>>G.vexnum>>G.arcnum;
printf("请输入顶点的值:");
for(int i = 0; i<G.vexnum; ++i){ //输入各点,构造表头结点表
cin>> G.vertices[i].data; //输入顶点值
G.vertices[i].firstarc=NULL; //初始化表头结点的指针域为NULL
}
for(int k = 0; k<G.arcnum;++k){ //输入各边,构造邻接表
int i,j;
char v1,v2;
ArcNode *p1 ;
ArcNode *p2 ;
printf("请输入(Vi,Vj)对应的顶点:");
cin>>v1>>v2; //输入一条边依附的两个顶点
i = LocateVex(G,v1); j = LocateVex(G,v2);
p1=new ArcNode; //生成一个新结点new ArcNode并把p1指向新结点
p1->adjvex=j; //p1所指向的顶点v2的位置
p1->nextarc=G.vertices[i].firstarc; //将新结点p1指向v1的邻接点
G.vertices[i].firstarc=p1; //将v1指向p1
p2=new ArcNode; //生成一个新结点new ArcNode并把p2指向新结点
p2->adjvex=i; //把新结点的adjvex赋值为v1顶点的位置
p2->nextarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=p2;
}
return OK;
}
void DFS_AL(ALGraph G, char v) //图G为邻接表类型
{
cout<<v; //访问第v个顶点
int x=LocateVex(G,v);
visited[x] = true;
ArcNode *p= G.vertices[x].firstarc; //p指向v的边链表的第一个边结点
int w;
while(p!=NULL){ //边结点非空
w=p->adjvex;
if(!visited[w]) DFS_AL(G,G.vertices[w].data); //如果w未访问,则递归调用DFS
p=p->nextarc; //p指向下一个边结点
}
}
void BFS(ALGraph G,char v)
{
int x=LocateVex(G,v);
LinkQueue Q;
InitQueue(Q); //辅助队列Q初始化,置空
EnQueue(Q,x); //v进队
while(Q.front!=Q.rear)
{
DeQueue(Q,x); //对头元素出队
if(!visited[x]) //x为未访问的邻接顶点
{
visited[x]=true;//访问x,并标记已被访问
cout<<G.vertices[x].data;
ArcNode *p=G.vertices[x].firstarc;
while(p!=NULL)
{
if(!visited[p->adjvex])
EnQueue(Q,p->adjvex);
p=p->nextarc;
}
}
}
}
int main(){
ALGraph G ;
char v;
CreateUDG(G);
printf("请输入出发顶点:");
cin>>v;
for(int i=0;i<G.vexnum;i++)
visited[i]=false;
printf("图的深度优先遍历为: ");
DFS_AL(G,v);
printf("\n");
for(int i=0;i<G.vexnum;i++)
visited[i]=false;
printf("图的广度优先遍历为: ");
BFS(G,v);
printf("\n");
return 0;
}
[测试数据]