数据结构实验报告之图(终极版本)

时间:2024.3.27

 《数据结构》实验报告 

           实验三 图的操作   

一、   需求分析   

1>实验目的

    1.掌握图的基本运算,如创建图、输出图、深度优先 遍历、广度优先遍历等。

 2. 掌握最小生成树、最短路径、关键路径等算法。

  2>实验内容

       1.编写程序,实现图的遍历。用邻接矩阵或者邻接表描述一个图,图中每个顶点代表一个字符,打印出深度优先搜索和广度优先搜索的节点序列。

       2.编写程序,输出上图的最小生成树的算法。

二、   概要设计 

1>抽象数据结构类型图的定义:

ADT Graph{

     数据对象V:V是具有相同特性的数据元素的集合,称为顶  点集。

数据关系R:

      R={VR}

     VR={<v,w>| v,w∈V且P(v,w),<v,w>表示从v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息}                                                  

  基本操作P:

     GreatGraph(&G,V,VR);

       初始条件:V是图的顶点集,VR是图中弧的集合。

       操作结果:按V和VR的定义构造图G。

     DestroyGraph(&G);

        初始条件:图G存在。

        操作结果:销毁图G。

     LocateVex (G,u);

         初始条件:图G已存在,u和G中的顶点有相同特征。

         操作结果:将L重置为空表。

      GetVex(G,v);

         初始条件:图G存在,v是G中某个顶点。

         操作结果:返回v的值。

     PutVex(&G,v,value);

        初始条件:图G存在,v是G中某个顶点。

        操作结果:对v赋值value。

     FirstAdjVex(G, v);

        初始条件:图G存在,v是G中某个顶点。

        操作结果:返回v的第一个邻接顶点。若顶点在G中没有邻接顶点,则返回“空”。

     NextAdjVex(G,v,w);

         初始条件:图G存在,v是G中某个顶点,w是v的邻接顶点。

         操作结果:返回v的下一个邻接顶点。若w是v的最后一个邻接顶点,则返回“空”。

     DESTTraverse(G,visit())

          初始条件:图G存在,visit是顶点的应用函数。

          操作结果:对图进行深度优先遍历。在遍历过程中对每个顶点调用函数visit一次且仅一次。一旦visit()失败,则操作失败。

}ADT  Graph

2>各程序模块之间的层次关系图:

 

 

 


   

 

 

 


          

 

   

三、   详细设计

   #include <stdio.h>

#include <iostream>

#include <malloc.h>

using namespace std;

#define int_max 10000

#define inf 9999

#define max 20

1.//…………………………………………邻接矩阵定义……………………

typedef struct ArcCell

{

 int adj;

 char *info;

}ArcCell,AdjMatrix[20][20];

typedef struct

{

 char vexs[20];

 AdjMatrix arcs;

 int vexnum,arcnum;

}MGraph_L;

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

int localvex(MGraph_L G,char v)//返回V的位置

{

 int i=0;

 while(G.vexs[i]!=v)

 {

  ++i;

 }

 return i;

}

2.创建图用邻接矩阵表示

int creatMGraph_L(MGraph_L &G)

{

 char v1,v2;

 int i,j,w;

 cout<<"…………创建无向图…………"<<endl<<"请输入图G顶点和弧的个数:"<<endl;

 cin>>G.vexnum>>G.arcnum;

 for(i=0;i!=G.vexnum;++i)

 {

  cout<<"输入顶点"<<i<<endl;

  cin>>G.vexs[i];

 }

 for(i=0;i!=G.vexnum;++i)

  for(j=0;j!=G.vexnum;++j)

  {

   G.arcs[i][j].adj=int_max;

   G.arcs[i][j].info=NULL;

  }

 for(int k=0;k!=G.arcnum;++k)

  {

   cout<<"输入一条边依附的顶点和权:"<<endl;

   cin>>v1>>v2>>w;//输入一条边依附的两点及权值

   i=localvex(G,v1);//确定顶点V1和V2在图中的位置

   j=localvex(G,v2);

   G.arcs[i][j].adj=w;

   G.arcs[j][i].adj=w;

  }

  cout<<"图G邻接矩阵创建成功!"<<endl;

  return G.vexnum;

}

void ljjzprint(MGraph_L G)

{

 int i,j;

 for(i=0;i!=G.vexnum;++i)

  {

   for(j=0;j!=G.vexnum;++j)

    cout<<G.arcs[i][j].adj<<" ";

   cout<<endl;

  }

}

int visited[max];//访问标记

int we;

typedef struct arcnode//弧结点

{

 int adjvex;//该弧指向的顶点的位置

 struct arcnode *nextarc;//弧尾相同的下一条弧

 char *info;//该弧信息

}arcnode;

typedef struct vnode//邻接链表顶点头接点

{

 char data;//结点信息

 arcnode *firstarc;//指向第一条依附该结点的弧的指针

}vnode,adjlist;

typedef struct//图的定义

{

 adjlist vertices[max];

 int vexnum,arcnum;

 int kind;

}algraph;

3.队列定义

typedef struct qnode

{

 int data;

 struct qnode *next;

}qnode,*queueptr;

typedef struct

{

 queueptr front;

 queueptr rear;

}linkqueue;

typedef struct acr

{

 int pre;//弧的一结点

 int bak;//弧另一结点

 int weight;//弧的权

}edg;

/4.用邻接表存储图

int creatadj(algraph &gra,MGraph_L G)

{

 

 int i=0,j=0;

 arcnode *arc,*tem,*p;

 for(i=0;i!=G.vexnum;++i)

 {

  gra.vertices[i].data=G.vexs[i];

  gra.vertices[i].firstarc=NULL;

 }

 for(i=0;i!=G.vexnum;++i)

 {

  for(j=0;j!=G.vexnum;++j)

  {

   if(gra.vertices[i].firstarc==NULL)

   {

    if(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)

    {

     arc=(arcnode *)malloc(sizeof(arcnode));

     arc->adjvex=j;

     gra.vertices[i].firstarc=arc;

     arc->nextarc=NULL;

     p=arc;

     ++j;

     while(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)

     {

      tem=(arcnode *)malloc(sizeof(arcnode));

      tem->adjvex=j;   

      gra.vertices[i].firstarc=tem;

      tem->nextarc=arc;

      arc=tem;

      ++j;

     }

     --j;

    }

   }

   else

   {

    if(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)

    {

     arc=(arcnode *)malloc(sizeof(arcnode));

     arc->adjvex=j;

     p->nextarc=arc;

     arc->nextarc=NULL;

     p=arc;

    }

   }

  }

 }

 gra.vexnum=G.vexnum;

 gra.arcnum=G.arcnum;

 /*for(i=0;i!=gra.vexnum;++i)

 {

  arcnode *p;

  cout<<i<<" ";

  p=gra.vertices[i].firstarc;

  while(p!=NULL)

  {

   cout<<p->adjvex;

   p=p->nextarc;

  }

  cout<<endl;

 }*/

  cout<<"图G邻接表创建成功!"<<endl;

 return 1;

}

void adjprint(algraph gra)

{

 int i;

 for(i=0;i!=gra.vexnum;++i)

 {

  arcnode *p;

  cout<<i<<" ";

  p=gra.vertices[i].firstarc;

  while(p!=NULL)

  {

   cout<<p->adjvex;

   p=p->nextarc;

  }

  cout<<endl;

 }

}

5.返回依附顶点V的第一个点

int firstadjvex(algraph gra,vnode v)

         //即以V为尾的第一个结点

{

 if(v.firstarc!=NULL)

 return v.firstarc->adjvex;

}

6.返回依附顶点V的相对于W的下一个顶点

int nextadjvex(algraph gra,vnode v,int w)

{

 arcnode *p;

 p=v.firstarc;

 while(p!=NULL&&p->adjvex!=w)

 {

   p=p->nextarc;

 }

  if(p->adjvex==w&&p->nextarc!=NULL)

  {

   p=p->nextarc;

    return p->adjvex;

  }

  if(p->adjvex==w&&p->nextarc==NULL)

  return -10;

}

7.始化队列

int initqueue(linkqueue &q)初

{

 q.rear=(queueptr)malloc(sizeof(qnode));

 q.front=q.rear;

 if(!q.front)

  return 0;

 q.front->next=NULL;

 return 1;

}

int enqueue(linkqueue &q,int e)//入队

{

 queueptr p;

 p=(queueptr)malloc(sizeof(qnode));

 if(!p)

  return 0;

 p->data=e;

 p->next=NULL;

 q.rear->next=p;

 q.rear=p;

 return 1;

}

int dequeue(linkqueue &q,int &e)//出队

{

 queueptr p;

 if(q.front==q.rear)

  return 0;

 p=q.front->next;

 e=p->data;

 q.front->next=p->next;

 if(q.rear==p)

  q.rear=q.front;

 free(p);

 return 1;

}

int queueempty(linkqueue q)//判断队为空

{

 if(q.front==q.rear)

  return 1;

 return 0;

}

8.广度优先遍历

void bfstra(algraph gra)

{

 int i,e;

 linkqueue q;

 for(i=0;i!=gra.vexnum;++i)

  visited[i]=0;

 initqueue(q);

 for(i=0;i!=gra.vexnum;++i)

  if(!visited[i])

  { visited[i]=1;

   cout<<gra.vertices[i].data;

   enqueue(q,i);

   while(!queueempty(q))

   {

    dequeue(q,e);

   // cout<<" "<<e<<" ";

    for(we=firstadjvex(gra,gra.vertices[e]);we>=0;we=nextadjvex(gra,gra.vertices[e],we))

    {

     if(!visited[we])

     {

      visited[we]=1;

      cout<<gra.vertices[we].data;

      enqueue(q,we);

     }

    }

   }

  }

}

int dfs(algraph gra,int i);//声明DFS

int dfstra(algraph gra)

{

 int i,j;

 for(i=0;i!=gra.vexnum;++i)

 {

  visited[i]=0;

 }

 for(j=0;j!=gra.vexnum;++j)

 {

  if(visited[j]==0)

   dfs(gra,j);

}

 return 0;

}

int dfs(algraph gra,int i)

{

 visited[i]=1;

 int we1;

 cout<<gra.vertices[i].data;

 for(we=firstadjvex(gra,gra.vertices[i]);we>=0;we=nextadjvex(gra,gra.vertices[i],we))

 {

  we1=we;

  if(visited[we]==0)

   dfs(gra,we);//<<endl;

  we=we1;

 }

 return 12;

}

typedef struct

 {

  int adjvex;

  int lowcost;

 }closedge;

9./最小生成树PRIM算法

int prim(int g[][max],int n)

{

 int lowcost[max],prevex[max]; //LOWCOST[]存储当前集合U分别到剩余结点的最短路径

         //prevex[]存储最短路径在U中的结点

 int i,j,k,min;

 for(i=2;i<=n;i++) //n个顶点,n-1条边

 {

  lowcost[i]=g[1][i]; //初始化

  prevex[i]=1; //顶点未加入到最小生成树中

 }

 lowcost[1]=0; //标志顶点1加入U集合

 for(i=2;i<=n;i++) //形成n-1条边的生成树

 {

  min=inf;

  k=0;

  for(j=2;j<=n;j++) //寻找满足边的一个顶点在U,另一个顶点在V的最小边

   if((lowcost[j]<min)&&(lowcost[j]!=0))

   {

    min=lowcost[j];

    k=j;

   }

  printf("(%d,%d)%d\t",prevex[k]-1,k-1,min);

  lowcost[k]=0; //顶点k加入U

  for(j=2;j<=n;j++) //修改由顶点k到其他顶点边的权值

   if(g[k][j]<lowcost[j])

   {

    lowcost[j]=g[k][j];

    prevex[j]=k;

   }

  printf("\n");

 } 

return 0;

}

int acrvisited[100];//kruscal弧标记数组

int find(int acrvisited[],int f)

{

 while(acrvisited[f]>0)

  f=acrvisited[f];

 return f;

}

void kruscal_arc(MGraph_L G,algraph gra)

{

 edg edgs[20];

 int i,j,k=0;

 for(i=0;i!=G.vexnum;++i)

  for(j=i;j!=G.vexnum;++j)

  {

   if(G.arcs[i][j].adj!=10000)

   {

    edgs[k].pre=i;

    edgs[k].bak=j;

    edgs[k].weight=G.arcs[i][j].adj;

    ++k;

   }

  }

 int x,y,m,n;

 int buf,edf;

 for(i=0;i!=gra.arcnum;++i)

  acrvisited[i]=0;

 for(j=0;j!=G.arcnum;++j)

 {

  m=10000;

  for(i=0;i!=G.arcnum;++i)

  {

   if(edgs[i].weight<m)

   {

    m=edgs[i].weight;

    x=edgs[i].pre;

    y=edgs[i].bak;

    n=i;

   }

  

  }

//  cout<<x<<y<<m;

//  cout<<endl;

   buf=find(acrvisited,x);

   edf=find(acrvisited,y);

//   cout<<buf<<" "<<edf<<endl;

   edgs[n].weight=10000;

   if(buf!=edf)

   {

    acrvisited[buf]=edf;

   

    cout<<"("<<x<<","<<y<<")"<<m;

    cout<<endl;

   }

 }

}

10.主函数

void main()

{

 algraph gra;

 MGraph_L G;

 int i,d,g[20][20];

 char a='a';

 d=creatMGraph_L(G);

 creatadj(gra,G);

 vnode v;

   cout<<endl<<"若该图为非强连通图(含有多个连通分量)时"<<endl

  <<"      最小生成树不存在,则显示为非法值。"<<endl<<endl;

 cout<<"菜单:"<<endl<<endl;

 cout<<"0、显示该图的邻接矩阵"<<endl;

 cout<<"1、显示该图的邻接表"<<endl;

 cout<<"2、深度优先遍历"<<endl;

 cout<<"3、广度优先遍历"<<endl;

 cout<<"4、最小生成树PRIM算法"<<endl;

 cout<<"5、最小生成树KRUSCAL算法"<<endl;

 int s;

 char y='y';

 while(y='y')

 {

  cout<<"请选择菜单:"<<endl;

  cin>>s;

  switch(s)

  {

  case 0:

   cout<<"邻接矩阵显示如下:"<<endl;

   ljjzprint(G);

   break;

  case 1:

   cout<<"邻接表显示如下:"<<endl;

   adjprint(gra);

   break;

  case 2:

   cout<<"广度优先遍历:";

   bfstra(gra);

   cout<<endl;

   break;

  case 3:

   for(i=0;i!=gra.vexnum;++i)

    {

    visited[i]=0;

   }

   cout<<"深度优先遍历:";

   dfstra(gra);

   cout<<endl;

   break;

  case 4:

   for(i=0;i!=G.vexnum;++i)

    for(int j=0;j!=G.vexnum;++j)

     g[i+1][j+1]=G.arcs[i][j].adj;

   cout<<"prim:"<<endl;

   prim(g,d);

   break;

  case 5:

   cout<<"kruscal:"<<endl;

   kruscal_arc(G,gra);

   break;

 

  }

  cout<<endl<<"是否继续?y/n:";

   cin>>y;

  if(y=='n')

   break;

 }

}

四、 调试分析:   

     1、由于开始对图的存储的知识还不熟识,在创建图的邻接矩阵时出现了许多问题。

     2、在寻找弧的弧点和对图的广度和深度进行优先遍历的时候时,有时因为自己的粗心,把指针的位置弄错了。

     3、做本程序用到了指针的知识,有时一有不懂的东西又只能打开书来看。

      4、图的广度优先遍历、深度优先遍历、最小生成树画图的方式很容易就可以做出来,却没想到真正用编程语言实现却是那么的难,在不断的查阅资料和不断的尝试下终于出结果了。     

五、   用户使用说明

1、  本程序的运行环境为Microsofe VC++ 6.0,执行文件为:***.cpp。

2、   进入界面后只需汇编、建立、执行则会弹出一黑色框,在鼠标闪动处输入你想要创建的图的顶点数和弧的数目,然后回车。

3、  然后挨个输入你所创建的图的顶点名。

4、  接着输入弧的两头和在该弧上的权值。

5、  接着按照菜单上给定的操作输入你所需要执行的操作的代码,一回车即可得到相应的结果。

6、  如需继续执行菜单里的操作,则在闪标处输入“y”,回车,在重复上一步的操作即可。

7、 若菜单里的操作均已执行完,则直接输入“n”,回车即可。

、测试结果

   

更多相关推荐:
数据结构实验报告

实验报告实验课程:数据结构实验项目:实验专业:计算机科学与技术姓名:**学号:***指导教师:**实验时间:20**-12-7重庆工学院计算机学院数据结构实验报告实验一线性表1.实验要求掌握数据结构中线性表的基…

数据结构实验报告(C语言)(强力推荐)

数据结构实验实验内容和目的掌握几种基本的数据结构集合线性结构树形结构等在求解实际问题中的应用以及培养书写规范文档的技巧学习基本的查找和排序技术让我们在实际上机中具有编制相当规模的程序的能力养成一种良好的程序设计...

数据结构实验———图实验报告

数据结构实验报告目的要求掌握图的存储思想及其存储实现掌握图的深度广度优先遍历算法思想及其程序实现掌握图的常见应用算法的思想及其程序实现实验内容键盘输入数据建立一个有向图的邻接表输出该邻接表3在有向图的邻接表的基...

数据结构实验报告格式

数据结构实验报告格式实验11顺序表的基本操作一实验目的1掌握使用VC上机调试线性表的基本方法2掌握线性表的基本操作插入删除查找等运算在顺序存储结构上的实现二实验内容顺序表的基本操作的实现三实验要求1认真阅读和理...

数据结构实验报告全集

数据结构实验报告全集实验一线性表基本操作和简单程序1实验目的1掌握使用VisualC60上机调试程序的基本方法2掌握线性表的基本操作初始化插入删除取数据元素等运算在顺序存储结构和链表存储结构上的程序设计方法2实...

数据结构实验报告5

计算机科学与工程系计算机科学与工程系2计算机科学与工程系附录可包括源程序清单或其它说明includeltiostreamgtincludeltstdiohgtusingnamespacestdtypedefst...

数据结构实验报告[4]

云南大学数据结构实验报告第四次实验学号姓名一实验目的复习线性表的逻辑结构存储结构及基本操作掌握顺序表和带头结点单链表了解有序表二实验内容必做题假设有序表中数据元素类型是整型请采用顺序表或带头结点单链表实现Ord...

数据结构实验报告

1实验一输入数据设为整型建立单链表并求相邻两节点data值之和为最大的第一节点例如输入2647300为结束符运行代码includequotstdiohquotincludequotmallochquottype...

数据结构实验报告

数据结构随堂实验实验报告指导教师姓名学号班级专业计算机科学与技术目录C语言结构体与指针1线性顺序表的实现及操作3串的匹配与替换6线性链表的实现及操作8栈和队列的应用13二叉树的实现及遍历21图的实现及遍历27查...

数据结构实验报告(C语言)(强力推荐)

数据结构实验实验内容和目的掌握几种基本的数据结构集合线性结构树形结构等在求解实际问题中的应用以及培养书写规范文档的技巧学习基本的查找和排序技术让我们在实际上机中具有编制相当规模的程序的能力养成一种良好的程序设计...

数据结构实验报告

实验目的1学会用先序创建一棵二叉树2学会采用递归算法对二叉树进行先序中序后序遍历3学会打印输出二叉树的遍历结果实验内容问题描述建立一棵二叉树并对其进行遍历先序中序后序打印输出遍历结果基本要求从键盘接受输入先序以...

安徽工业大学数据结构实验报告

安徽工业大学计算机学院数据结构实验报告姓名学号班级教师内容线性表基本操作的实现栈的基本操作串的模式匹配二叉树操作图的创建与遍历20xx525实验一线性表基本操作的实现一实验目的1掌握使用TurboC20上机调试...

数据结构实验报告(46篇)