数据结构
实验 图的存储与遍历
一、实验目的
1、熟练掌握图的存储结构。
2、熟练掌握图的遍历及基本操作的实现。
二、实验原理
1、图存储结构及其应用。
三、实验设备
Win系统电脑一台
四、实验过程(程序清单)
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Status int
#define VRType int
#define InfoType char
#define VertexType char
#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
#define INFINITY 0 //最大值
#define MAX_VERTEX_NUM 20 //最大顶点个数
using namespace std;
bool visited[MAX_VERTEX_NUM];//访问标志数组
typedef struct ArcCell{//邻接矩阵(元素)类型
VRType adj; //顶点关系(边),图用1\0表示是否相邻,网为权值
InfoType *info; //边的相关信息指针
} ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct{//图的邻接矩阵类型
VertexType vexs[MAX_VERTEX_NUM]; //顶点向量
AdjMatrix arcs; //邻接矩阵
int vexnum,arcnum; //图的顶点数和边数
} Mgraph;
void (*VisitFunc)(Mgraph G,int v);//函数变量
void Input(InfoType *info){//录入边
info=(char*)malloc(sizeof(char));
scanf("%c",&*info);
}
Status LocateVex(Mgraph &G,VertexType u){//若G中存在点u,则返回该点在图中的位置
int Loc;
for(Loc=0;Loc<G.vexnum;Loc++){
if(G.vexs[Loc]==u){
return Loc;
}
}
return -1;
}
Status CreateDG(Mgraph &G){//采用邻接矩阵表示法,构造有向图G
int IncInfo,i,j,k;
char v1,v2;
printf("请输入图的顶点数:");
scanf("%d",&G.vexnum);
getchar();
printf("请输入图的边数:");
scanf("%d",&G.arcnum);
getchar();
printf("各边是否含有其他信息(1(有),0(没有):");
scanf("%d",&IncInfo);
getchar();
for(i=0;i<G.vexnum;i++){
printf("请输入第%d个顶点:",i+1);
scanf("%c",&G.vexs[i]);
getchar();
}
for(i=0;i<G.vexnum;i++){
for(j=0;j<G.vexnum;j++){
G.arcs[i][j].adj=INFINITY;
G.arcs[i][j].info=NULL;
}
}
for(k=0;k<G.arcnum;k++){
printf("请输入一条边连接的顶点:\n");
printf("边的尾顶点:");//输入一条边依附的顶点
scanf("%c",&v1);
getchar();
printf("边的头顶点:");
scanf("%c",&v2);
getchar();
i=LocateVex(G,v1);
j=LocateVex(G,v2);//确定v1和v2在G中的位置
G.arcs[i][j].adj=1;//v1和v2的连接关系
if(IncInfo){
Input(G.arcs[i][j].info);//若边含有相关信息,则输入
}
}
return OK;
}//CreateDG
Status CreateDN(Mgraph &G){//采用邻接矩阵表示法,构造有向网G
int IncInfo,i,j,k,w;
char v1,v2;
printf("请输入图的顶点数:");
scanf("%d",&G.vexnum);
getchar();
printf("请输入图的边数:");
scanf("%d",&G.arcnum);
getchar();
printf("各边是否含有其他信息(1(有),0(没有)):");
scanf("%d",&IncInfo);
getchar();
for(i=0;i<G.vexnum;++i){ //构造顶点向量
printf("请输入第%d个顶点:",i+1);
scanf("%c",&G.vexs[i]);
getchar();
}
for(i=0;i<G.vexnum;++i){ //初始化邻接矩阵
for(j=0;j<G.vexnum;++j){
G.arcs[i][j].adj=INFINITY;
G.arcs[i][j].info=NULL;
}
}
for(k=0;k<G.arcnum;++k){ //构造邻接矩阵
printf("请输入一条边连接的顶点及权值:\n");
printf("边的尾顶点:");//输入一条边连接的顶点及权值
scanf("%c",&v1);
getchar();
printf("边的头顶点:");
scanf("%c",&v2);
getchar();
printf("权值:");
scanf("%d",&w);
getchar();
i=LocateVex(G,v1);
j=LocateVex(G,v2);//确定v1和v2在G中的位置
G.arcs[i][j].adj=w;//边<v1,v2>的权值
if(IncInfo){
Input(G.arcs[i][j].info);//若边含有相关信息,则输入
}
}
return OK;
}//CreateDN
Status CreateUDG(Mgraph &G){//采用邻接矩阵表示法,构造无向图G
int IncInfo,i,j,k;
char v1,v2;
printf("请输入图的顶点数:");
scanf("%d",&G.vexnum);
getchar();
printf("请输入图的边数:");
scanf("%d",&G.arcnum);
getchar();
printf("各边是否含有其他信息(1(有),0(没有)):");
scanf("%d",&IncInfo);
getchar();
for(i=0;i<G.vexnum;++i){ //构造顶点向量
printf("请输入第%d个顶点:",i+1);
scanf("%c",&G.vexs[i]);
getchar();
}
for(i=0;i<G.vexnum;++i){ //初始化邻接矩阵
for(j=0; j<G.vexnum; ++j){
G.arcs[i][j].adj=INFINITY;
G.arcs[i][j].info=NULL;
}
}
for(k=0;k<G.arcnum;++k){//构造邻接矩阵
printf("请输入一条边连接的顶点:\n");
printf("边的一个顶点:");//输入一条弧依附的顶点
scanf("%c",&v1);
getchar();
printf("边的另一个顶点:");
scanf("%c",&v2);
getchar();
i=LocateVex(G,v1);
j=LocateVex(G,v2);//确定v1和v2在G中的位置
G.arcs[i][j].adj=1;//v1和v2的连接关系
if(IncInfo){
Input(G.arcs[i][j].info);//若边含有相关信息,则输入
}
G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称边<v2,v1>
}
return OK;
}//CreateUDG
Status CreateUDN(Mgraph &G){//采用邻接矩阵表示法,构造无向网G
int IncInfo,i,j,k,w;
char v1,v2;
printf("请输入图的顶点数:");
scanf("%d",&G.vexnum);
getchar();
printf("请输入图的边数:");
scanf("%d",&G.arcnum);
getchar();
printf("各边是否含有其他信息(1(有),0(没有)):");
scanf("%d",&IncInfo);
getchar();
for(i=0;i<G.vexnum;++i){
printf("请输入第%d个顶点:",i+1);
scanf("%c",&G.vexs[i]);
getchar();
}//构造顶点向量
for(i=0; i<G.vexnum; ++i){
for(j=0;j<G.vexnum;++j){
G.arcs[i][j].adj=INFINITY;
G.arcs[i][j].info=NULL;
}
}//初始化邻接矩阵
for(k=0;k<G.arcnum;++k){ //构造邻接矩阵
printf("请输入一条边连接的顶点及权值:\n");
printf("边的一个顶点:");//输入一条边连接的顶点及权值
scanf("%c",&v1);
getchar();
printf("边的另一个顶点:");
scanf("%c",&v2);
getchar();
printf("权值:");
scanf("%d",&w);
getchar();
i=LocateVex(G,v1);
j=LocateVex(G,v2);//确定v1和v2在G中的位置
G.arcs[i][j].adj=w;//边<v1,v2>的权值
if(IncInfo){
Input(G.arcs[i][j].info);//若边含有相关信息,则输入
}
G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称边<v2,v1>
}
return OK;
}//CreateUDN
Status FirstAdjVex(Mgraph G,int v){//返回v的第一个邻接顶点
int i;
if(v>=0 && v<G.vexnum){
for(i=0;i<G.vexnum;i++){
if(G.arcs[v][i].adj){
return i;
}
}
}
return -1;
}//FirstAdjVex
Status NextAdjVex(Mgraph G,int v,int w){//返回v的(相对于w的)下一个邻接顶点
int i;
if(v>=0 && v<G.vexnum){
if(w>=0 && w<G.vexnum){
for(i=w+1;i<G.vexnum;i++){
if(G.arcs[v][i].adj){
return i;
}
}
}
}
return -1;
}//NextAdjVex
void Print_JZ(Mgraph G){//打印矩阵
int i,j;
for(i=0;i<G.vexnum;i++){
for(j=0;j<G.vexnum;j++){
printf("%3d ",G.arcs[i][j].adj);
}
printf("\n");
}
}
void Print(Mgraph G,int v){
printf("%c",G.vexs[v]);
}
int main(){
char Ch;
Mgraph G;
while(1){
printf("*********图**********\n");
printf("******1.建立有向图***\n");
printf("******2.建立有向网***\n");
printf("******3.建立无向图***\n");
printf("******4.建立无向网***\n");
printf("******0.返回*********\n");
printf("输入图的种类:");
scanf("%c",&Ch);
getchar();
switch(Ch)
{
case '1':
CreateDG(G);//构造有向图G
printf("有向图G的邻接矩阵为:\n");
Print_JZ(G);
getchar();
break;
case '2':
CreateDN(G);//构造有向网G
printf("有向网G的邻接矩阵为:\n");
Print_JZ(G);
getchar();
break;
case '3':
CreateUDG(G);//构造无向图G
printf("无向图G的邻接矩阵为:\n");
Print_JZ(G);
getchar();
break;
case '4':
CreateUDN(G);//构造无向网G
printf("无向网G的邻接矩阵为:\n");
Print_JZ(G);
getchar();
break;
case '0':
exit(0);
break;
default:
printf("选择错误!");
}
return 0;
}
}
图的遍历(深度优先遍历/广度优先遍历)
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
#define OK 1
#define ERROR 0
#define INFINITY INT_MAX//最大值“无穷”
#define MAX_VERTEX_NUM 20//最大顶点个数
typedef int Status;
typedef int Boolean;
typedef char VertexType[20];
typedef int VRType;
//队列的操作
//队列的类型定义
typedef int QElemType;
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//初始化队列
Status InitQueue(LinkQueue *Q){
(*Q).front=(*Q).rear=(QueuePtr)malloc(sizeof(QNode));
if(!(*Q).front){
exit(OVERFLOW);
}
(*Q).front->next=NULL;
return OK;
}
//判断队列是否为空
Status QueueEmpty(LinkQueue Q){
if(Q.front==Q.rear){
return TRUE;
}
else{
return FALSE;
}
}
//入队列
Status EnQueue(LinkQueue *Q,QElemType e){
QueuePtr p;
p=(QueuePtr)malloc(sizeof(QNode));
if(!p){
exit(OVERFLOW);
}
p->data=e; p->next=NULL;
(*Q).rear->next=p;
(*Q).rear=p;
return OK;
}
//出队列
Status DeQueue(LinkQueue *Q, QElemType *e){
QueuePtr p;
if((*Q).front==(*Q).rear){
return ERROR;
}
p=(*Q).front->next;
*e=p->data;
(*Q).front->next=p->next;
if((*Q).rear==p){
(*Q).rear=(*Q).front;
}
free(p);
return OK;
}
//图的操作
//图的类型定义
typedef struct ArcCell{
VRType adj;//图中有1/0表示是否有边,网中表示边上的权值
// InfoType *info;//与边相关的信息
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct{
VertexType vexs[MAX_VERTEX_NUM];//顶点向量
AdjMatrix arcs;//邻接矩阵
int vexnum,arcnum;//图中当前顶点数和边数
}MGraph;
//顶点在顶点向量中的定位
int LocateVex(MGraph G,VertexType v){
int i;
for(i=0;i<G.vexnum;i++){
if(strcmp(v,G.vexs[i])==0){
break;
}
}
return i;
}
//建立无向图的邻接矩阵
void CreateGraph(MGraph *G){
int i,j,k; VertexType v1,v2;
printf("输入图的顶点数和边数:");
scanf("%d %d",&(*G).vexnum,&(*G).arcnum);
printf("输入各顶点:\n",(*G).vexnum);
for(i=0;i<(*G).vexnum;i++){//输入顶点向量
scanf("%s",(*G).vexs[i]);
}
for(i=0;i<(*G).vexnum;i++){//邻接矩阵初始化
for(j=0;j<(*G).vexnum;j++){
(*G).arcs[i][j].adj=0;
}
}
printf("输入各边的结点为:\n",(*G).arcnum);
for(k=0;k<(*G).arcnum;k++){ //输入无权图的边
scanf("%s %s",v1,v2);
i=LocateVex(*G,v1);j=LocateVex(*G,v2);
(*G).arcs[i][j].adj=1;
(*G).arcs[j][i]=(*G).arcs[i][j];
}
}
//查找第1个邻接点
int FirstAdjVex(MGraph G,int v){
int j,p=-1;
for(j=0;j<G.vexnum;j++){
if(G.arcs[v][j].adj==1){
p=j; break;
}
}
return p;
}
//查找下一个邻接点
int NextAdjVex(MGraph G,int v,int w){
int j,p=-1;
for(j=w+1;j<G.vexnum;j++){
if (G.arcs[v][j].adj==1){
p=j; break;
}
}
return p;
}
//深度遍历
Boolean visited[MAX_VERTEX_NUM]; //设置全局的访问标志数组
void DFS(MGraph G, int v){
int w;
visited[v]=TRUE;
printf("%s",G.vexs[v]);
for(w=FirstAdjVex(G,v); w>=0; w=NextAdjVex(G,v,w)){
if(!visited[w]){
DFS(G,w);
}
}
}
void DFSTraverse(MGraph G){
int v;
for(v=0;v<G.vexnum;v++){
visited[v]=FALSE;
}
for(v=0; v<G.vexnum; v++){
if (!visited[v]){
DFS(G,v);
}
}
}
//广度遍历
void BFSTraverse(MGraph G){
int v,u,w; LinkQueue Q;
for(v=0; v<G.vexnum; v++){
visited[v]=FALSE;
}
InitQueue(&Q);
for(v=0; v<G.vexnum; v++){
if (!visited[v]){
visited[v]=TRUE;
printf("%s",G.vexs[v]);
EnQueue(&Q,v);
while(!QueueEmpty(Q)){
DeQueue(&Q,&u);
for(w=FirstAdjVex(G,u); w>=0; w=NextAdjVex(G,u,w)){
if (!visited[w]){
visited[w]=TRUE;
printf("%s",G.vexs[w]);
EnQueue(&Q,w);
}
}
}
}
}
}
//主函数
int main(){
int w;
MGraph G;
CreateGraph(&G);
printf("\n深度优先遍历:"); DFSTraverse(G);//深度遍历
printf("\n广度优先遍历:"); BFSTraverse(G);//广度遍历
}
拓扑排序
#include<stdio.h>
#include<stdlib.h>
#define MAX_VEXTEX_NUM 20 //最大顶点个数#define M 20
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define M 20
#define ERROR 0
typedef int ElemType;
typedef struct ArcNode{//定义表结点结构
int adjvex; //与vi相邻接的顶点编号
struct ArcNode *nextarc; //指向下一条弧(边)的指针
}ArcNode;
typedef struct VNode{//定义表头结点结构
int data;
ArcNode *firstarc; //指向第一条弧(边)的指针
}VNode, AdjList[MAX_VEXTEX_NUM];
typedef struct{ //定义邻接表结构
AdjList vertices; //表头结点数组
int vexnum, arcnum; //顶点和弧(边)的个数
}ALGraph;
typedef struct{ //构件栈
ElemType *base;
ElemType *top;
int stacksize;
}SqStack;
void InitStack(SqStack *); //函数声明
int Pop(SqStack *, ElemType *);
void Push(SqStack *, ElemType);
int StackEmpty(SqStack *);
void CreatGraph(ALGraph *);
void FindInDegree(ALGraph, int *);
void TopologicalSort(ALGraph);
void InitStack(SqStack *S){//初始化栈
S->base=(ElemType *)malloc(STACK_INIT_SIZE * sizeof (ElemType));
if(!S->base){
printf("分配失败");
exit(1);
}
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
}
int Pop(SqStack *S,ElemType *e){//出栈操作
if(S->top==S->base){
return ERROR;
}
*e=*--S->top;
return 0;
}
void Push(SqStack *S, ElemType e){//进栈操作
if (S->top - S->base >= S->stacksize){
S->base=(ElemType *)realloc(S->base,(S->stacksize+STACKINCREMENT) * sizeof(ElemType));
if (!S->base){
printf("分配失败");
exit(1);
}
S->top=S->base+S->stacksize;
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;
}
int StackEmpty(SqStack *S){//判断栈是否为空
if(S->top==S->base){
return OK;
}
else{
return ERROR;
}
}
void CreatGraph(ALGraph *G){//构建图
int m,n,i;
ArcNode *p;
printf("请输入顶点数和边数:");
scanf("%d %d",&G->vexnum,&G->arcnum);
for(i=1;i<=G->vexnum;i++){
G->vertices[i].data=i;
G->vertices[i].firstarc=NULL;
}
for(i=1;i<=G->arcnum;i++){ //输入存在边的点集合
printf("请输入存在边的两个顶点的序号:");
scanf("%d %d", &n, &m);
while(n<0 || n>G->vexnum || m<0 || m>G->vexnum){
printf("输入的顶点序号不正确,请重新输入:");
scanf("%d %d",&n,&m);
}
p=(ArcNode*) malloc(sizeof (ArcNode));
if(p==NULL){
printf("分配失败");
exit(1);
}
p->adjvex=m;
p->nextarc=G->vertices[n].firstarc;
G->vertices[n].firstarc=p;
}
}
void FindInDegree(ALGraph G, int indegree[]){//找入度为零的节点
int i;
for(i=1;i<=G.vexnum;i++){
indegree[i]=0;
}
for(i=1;i<=G.vexnum;i++){
while(G.vertices[i].firstarc){
indegree[G.vertices[i].firstarc->adjvex]++;
G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
}
}
}
//进行拓扑排序 ,拓扑排序结果不唯一
void TopologicalSort(ALGraph G){
int indegree[M];
int i,k,n;
int count=0;
ArcNode *p;
SqStack S;
FindInDegree(G,indegree);
InitStack(&S);
for(i=1;i<=G.vexnum;i++) {
if(!indegree[i]){
Push(&S,i);
}
}
printf("进行拓扑排序输出顺序为:\n"); //输出结果
while(!StackEmpty(&S)){
Pop(&S,&n);
printf("%4d",G.vertices[n].data);
count++;
for(p=G.vertices[n].firstarc;p!=NULL;p=p->nextarc){
k=p->adjvex;
if(!(--indegree[k])){
Push(&S, k);
}
}
}
printf("\n");
if(count<G.vexnum){
printf("该有向图有回路");
}
else{
printf("排序成功\n");
}
}
int main(void){ //主函数
ALGraph G;
CreatGraph(&G);
TopologicalSort(G);
system("pause");
return 0;
}
五、实验结果
图的遍历(深度优先遍历/广度优先遍历)
拓扑排序
六、实验心得
1、拓扑排序的结果不一定唯一。
2、掌握图的深度优先遍历、广度优先遍历、拓扑排序。
3、掌握有向图、有向网、无向图、无向网的邻接矩阵。