数据结构实验报告无向图邻接矩阵存储结构

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数学与计算机学院
课程设计说明书
课程名称: 数据结构与算法课程设计
课程代码: 6014389
题目: 无向图的邻接矩阵存储结构
年级/专业/班: 2018级软件4班
学生姓名: 吴超
学号: 312018*********
开始时间: 2018年12月9日
完成时间: 2018年12月30日
课程设计成绩:
学习态度及平时技术水平与实际创新<5)说明书<计算书、图纸、分析总分
指导教师签名:年月日
数据结构课程设计任务书
学院名称:数学与计算机学院课程代码:__6014389______
专业:软件工程年级: 2018
一、设计题目
无向图的邻接矩阵存储结构
二、主要内容
图是无向带权图,对下列各题,要求写一算法实现。

1)能从键盘上输入各条边和边上的权值;
2)构造图的邻接矩阵和顶点集。

3)输出图的各顶点和邻接矩阵
4)插入一条边
5)删除一条边
6)求出各顶点的度
7)判断该图是否是连通图,若是,返回1;否则返回0.
8)使用深度遍历算法,输出遍历序列。

三、具体要求及应提交的材料
用C/C++语言编程实现上述内容,对每个问题写出一个算法实现,并按数学与计算机学院对课程设计说明书规范化要求,写出课程设计说明书,并提交下列材料:
1>课程设计说明书打印稿一份
2>课程设计说明书电子稿一份;
3>源程序电子文档一份。

四、主要技术路线提示
用一维数组存放图的顶点信息,二维数组存放各边信息。

五、进度安排
按教案计划规定,数据结构课程设计为2周,其进度及时间大致分配如下:
[1] 严蔚敏,吴伟民.数据结构.清华大学出版社出版。

[2] 严蔚敏,吴伟民. 数据结构题集(C语言版> .清华大学出版社.2003年5月。

[3]唐策善,李龙澎.数据结构(作C语言描述> .高等教育出版社.2001年9月
[4] 朱战立.数据结构(C++语言描述><第二版本).高等出版社出版.2004年4月
[5]胡学钢.数据结构(C语言版> .高等教育出版社.2004年8月
指导教师签名日期年月日
系主任审核日期年月日
目录
摘要
随着计算机的普及,涉及计算机相关的科目也越来越普遍,其中数据结构是计算机专业重要的专业基础课程与核心课程之一,为适应我国计算机科学技术的发展和应用,学好数据结构非常必要,然而要掌握数据结构的知识非常难,所以对“数据结构”的课程设计比不可少。

本说明书是对“无向图的邻接矩阵存储结构”课程设计的说明。

首先是对需求分析的简要阐述,说明系统要完成的任务和相应的分析,并给出测试数据。

其次是概要设计,说明所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次关系,以及ADT描述。

然后是详细设计,描述实现概要设计中定义的基本功操作和所有数据类型,以及函数的功能及代码实现。

再次是对系统的调试分析说明,以及遇到的问题和解决问题的方法。

然后是用户使用说明书的阐述,然后是测试的数据和结果的分析,最后是对本次课程设计的结论。

关键词:网络化;计算机;对策;图;储存。

引言
数据结构是计算机存储、组织数据的方式。

数据结构是指相互之间存在一种或多种特定关系的的集合。

通常情况下,精心选择的数据结构可以带来更高的运行或者存储。

数据结构往往同高效的检索算法和技术有关。

选择了数据结构,算法也随之确定,是数据而不是算法是系统构造的关键因素。

这种洞见导致了许多种方法和的出现,语言就是其中之一。

此次课程设计根据课堂讲授内容,下发任务书,要求学生完成相应系统,以消化课堂所讲解的内容;通过调试典型例题或习题积累调试C++程序从而获得数据结构的编程经验;通过完成此项课程设计,逐渐培养学生的编程能力、用计算机解决实际问题的能力,并充分理解图的矩阵储存方法。

此次课程设计题目为《无向图的邻接矩阵存储结构》,所利用工具为 Microsoft visual studio 2008.
1需求分析
随着计算机的普及,信息的存储逐渐和我们的日常生活变得密切起来,而数据的存储方式也多种多样,比如树、链表、数组、图等等。

为了充分体现图的矩阵储存结构的优势与功能,要求本系统应达到以下要求:
1.图是无向带权图
2.能从键盘上输入各条边和边上的权值;
3.构造图的邻接矩阵和顶点集。

4.输出图的各顶点和邻接矩阵
5.插入一条边
6.删除一条边
7.求出各顶点的度
8.判断该图是否是连通图,若是,返回1;否则返回0.
9.使用深度遍历算法,输出遍历序列。

1.1任务与分析
邻接矩阵是表示图形中顶点之间相邻关系的矩阵。

设G=(V,E>是具有n个顶点的图,则G 的邻接矩阵是n阶方阵。

为了实现此算法,用一维数组a[]存放图的顶点信息,二维数组b[][]存放各边信息。

顶点或者边存在,则该数组应有值,通过此来进行建立、插入、删除。

其余方法也将能通过数组的特性来实现。

1.2测试数据
1.建立图的矩阵存储结构,第一次建立连通图A1,第二次建立非连通图A2。

如下:
图1.1测试数据
2.选择输出图
3.选择插入节点,插入4
4.选择插入边,在3,4节点中插入边,权值为55
5.选择深度优先搜索
6.选择判断是否连通
7.选择求最小生成树
8.选择求各顶点的度
9.选择退出,重新运行,此次建立A2的图,再次进行测试。

2 概要设计
2.1 ADT描述
ADT Glist
{
{VR}={图的顶点和边}
VR={<v,w> | v,w∈V, <v,w>表示顶点v和w间的边;} 基本操作:
初始化空图;
输入建立图;
深度优先遍历图;
确定图中的顶点数目;
确定图中边的数目;
在图中插入一个顶点;
在图中插入一条边;
删除图中一个顶点
删除图中的一条边;
求顶点的度;
求最小生成树;
} ADT Graph。

2.2程序模块结构
图2.1:模块结构
2.2.1 结构体定义
本系统未采用结构体方法,类的定义如下:
定义顶点: nodecount,edgecount 边:已经分别存放顶点和边的两个数组: a[MaxNode]和b[MaxNode][MaxNode]。

其余成员函数均以public形式声明。

在邻接矩阵表示的图中,顶点信息用一维数组表示a[]。

在简单情况下可省略,仅以下标值代表顶点序号。

若需要,顶点信息更加丰富。

边<或弧)信息用二维数组表示b[ ][ ],这也是邻接矩阵。

包含边的权值。

在类中数据成员有4个,重要的是邻接矩阵Edge[ ][ ]、总边数edgecount和顶点数nodecount。

class Graph1
{
private:
int nodecount。

//节点
int edgecount。

//边
int a[MaxNode]。

//顶点信息组
//set<int> a。

int b[MaxNode][MaxNode]。

//权值信息组
public:
Graph1(int>。

//构造函数
int getNodeCount(>。

//当前的节点数
int getEdgeCount(>。

//当前的边数
void insertNode(int>。

//插入一个节点
void isertEdge(int ,int ,int>。

//插入一条边
void deleteEdge(int,int>。

//删除一条边
bool isliantong(>。

//判断是否连通
int getWeight(int,int>。

//获得某条边的权值
int Depth(int >。

//深度遍历准备,用于建立顶点访问数组和记录所访问顶点个数
void Depth(int v,int visited[],int &n>。

//深度遍历
void outDu(Graph1 G>。

//输出节点个数
void PrintOut(Graph1 G> 。

//输出图
void CreatG(int n,int e>。

//建立图
}。

2.3各功能模块
以下将以注释形式为每个函数的功能进行声明:
构造函数:Graph1(int> 用于初始化图
get函数:int getNodeCount(>。

得到当前的节点数
get函数:int getWeight(int,int>。

获得某条边的权值
get函数:int getEdgeCount(>。

得到当前的边数
插入函数:void insertNode(int>。

插入一个节点
插入函数:void isertEdge(int ,int ,int>。

插入一条边
删除函数:void deleteEdge(int,int>。

删除一条边
判断函数:bool isliantong(>。

判断是否连通
遍历函数1:int Depth(int >。

//深度遍历准备,用于建立顶点访问数组和记录所访问顶点个数
遍历函数2:void Depth(int v,int visited[],int &n>。

//深度遍历
求度函数:void outDu(Graph1 G>。

输出节点个数
输出函数:void PrintOut(Graph1 G> 。

输出图
构建函数:void CreatG(int n,int e>。

建立图
3 详细设计
3.1类的定义
class Graph1
{
private:
int nodecount。

//节点
int edgecount。

//边
int a[MaxNode]。

//顶点信息组
//set<int> a。

int b[MaxNode][MaxNode]。

//权值信息组
public:
Graph1(int>。

//构造函数
int getNodeCount(>。

//当前的节点数
int getEdgeCount(>。

//当前的边数
void insertNode(int>。

//插入一个节点
void isertEdge(int ,int ,int>。

//插入一条边
void deleteEdge(int,int>。

//删除一条边
void prim(int>。

//生成最小树
bool isliantong(>。

//判断是否连通
int getWeight(int,int>。

//获得某条边的权值
int Depth(int >。

//深度遍历准备,用于建立顶点访问数组和记录所访问顶点个数
void Depth(int v,int visited[],int &n>。

//深度遍历
void outDu(Graph1 G>。

//输出节点个数
void PrintOut(Graph1 G> 。

//输出图
void CreatG(int n,int e>。

//建立图
}。

3.2 初始化
初始化邻接矩阵以及有关参数,通过for循环将数组的值都初始化为0,使之成为一个空图。

Graph1::Graph1(int s=MaxNode>//构造函数
{
for(int i=0。

i<=s-1。

i++>
for(int j=0。

j<=s-1。

j++>
b[i][j]=0。

nodecount=0。

for(int k=0。

k<=s-1。

k++>
a[k]=-1。

}
3.3 图的构建操作
在主函数中要求输入需要构建的图的顶点数和边数,调用构建函数,分别用两个for语句来构建图<即输入顶点的值和边的权值),此处要求输入有一定的顺序,不能随意输入。

void Graph1::CreatG(int n,int e>
{ int i,vi,vj,w。

edgecount=e。

nodecount=n。

cout<<endl<<" 输入顶点的信息<暂设为整型):" 。

for ( i=0。

i<nodecount。

i++ >
{ cout<<"\n "<<i+1<<": "。

cin>>a[i]。

}
for ( i=0。

i<edgecount。

i++ > //输入两个顶点编号和边权值
{ cout<<endl<<" 输入边的信息<vi,vj,w):"<<endl。

cin>>vi>>vj>>w。

b[vi-1][vj-1]=w。

b[vj-1][vi-1]=w。

}
3.4 输出操作
本函数通过传过来的对象G得到相关数组,通过for循环来分别输出顶点数组和边的权值数组<邻接矩阵)。

void Graph1::PrintOut(Graph1 G>
{ int i。

cout<<"\n 输出顶点的信息:"<<endl。

for ( i=0。

i<G.getNodeCount(>。

i++ > cout<<G.a[i]<<" "。

cout<<endl<<"\n 输出邻接矩阵:" 。

for ( i=0。

i<G.getNodeCount(>。

i++ >
{ cout<<endl<<i+1<<": "。

for ( int j=0。

j<G.getNodeCount(> 。

j++ > cout<<G.b[i][j]<<" "。

cout<<endl。

}
}
3.5 get操作
用于得到图的顶点数、边数、权值
int Graph1::getNodeCount(>//当前的节点数
{
return nodecount。

}
int Graph1::getEdgeCount(>//当前的边数
{
return edgecount。

}
int Graph1::getWeight(int x,int y>//获得某条边的权值
{
return b[x-1][y-1]。

}
3.6 插入操作
插入顶点:通过将传来的值<顶点值)赋到一个当前顶点数下一个的数组元素中实现插入功能,此时顶点树加1。

插入边:原理与插入顶点相同,通过传过来的两个顶点信息与权值进行相应赋值,不过此时应该注意表示同一条边的两个数组都该赋值。

void Graph1::insertNode(int it>//插入一个节点
a[nodecount++]=it。

cout<<"当前节点数为:"<<nodecount<<endl。

}
void Graph1::isertEdge(int x,int y,int w>//插入一条边
{
b[x-1][y-1]=w。

b[y-1][x-1]=w。

cout<<"该边插入成功! "<<endl。

edgecount++。

}
3.7 删除操作
将相应的数组值赋为0从而达到删除目的。

void Graph1::deleteEdge(int x ,int y>//删除一条边
{
b[x-1][y-1]=0。

b[y-1][x-1]=0。

cout<<"边("<<x<<","<<y<<">已经成功删除!"。

edgecount--。

}
3.8 求顶点的度操作
通过两个for循环来查找各顶点,判断其是否有边<权值),有边的话又有几条边,每找到一条边则度数加一,记录到存放顶点的度数的数组M[]中,搜索完成后输出各顶点的度。

void Graph1::outDu(Graph1 G>//输出各点的度
{
int m[Max]={0},k,i。

for(k=0。

k<G.getNodeCount(>。

k++>
for(i=0。

i<G.getNodeCount(>。

i++>
{
if(G.b[k][i]!=0>
m[k]++。

}
cout<<"各点的度:"<<endl。

for(i=0。

i<G.getNodeCount(>。

i++>
{
cout<<"顶点"<<i<<"的度:"<<m[i]<<endl。

}
}
3.9 深度遍历操作
图的深度优先遍历DFS算法是沿着某初始顶点出发的一条路径,尽可能深入地前进,即每次在访问完当前顶点后,首先访问当前顶点的一个未被访问过的邻接顶点,然后去访问这个邻接点的一个未被访问过的邻接点,这是一个递归算法。

其中第一个遍历函数Depth(int node>其主要作用是构建访问数组intv[],以及调用核心遍历函数Depth(int v,int visited[],int &n>,其中的n是用来记录访问的顶点数目,用于判断图的连通性。

int Graph1::Depth(int node>
{
int n=0。

int v[MaxNode]。

for(int i=0。

i<=nodecount-1。

i++>
v[i]=0。

Depth(node,v,n>。

return n。

//n记录访问节点数,用于判断是否连通
}
void Graph1::Depth(int v,int visited[],int &n>
{ cout<<" "<<v+1<<" " 。

//访问顶点v
visited[v]=1。

//标记顶点v已访问
n++。

//n记录访问节点数
for (int col=0。

col<nodecount。

col++>
{ if (b[v][col]==0||b[v][col]==Max>
continue。

//找v一个邻接点col
if (!visited[col]> Depth(col, visited,n>。

//调用深度递归遍历
}
}
3.10 判断连通操作
通过调用遍历函数得到所能访问的顶点数n,然后判断n与当前顶点数是否相等来确认是否连通。

bool Graph1::isliantong(>//判断是否连通
{
int n=0。

n=Depth(0>。

cout<<"该图的总节点数为:"<<nodecount<<"!"<<endl。

cout<<"其中一个连通分量连通的节点数为:"<<n<<"!"<<endl。

if(n==nodecount>//访问到的节点数与顶点数是否相等
return 1。

else return 0。

}
3.11 主函数
主函数的主要功能是引导构建新图的邻接矩阵存储结构,并且制作菜单、调用各个相应的功能函数。

int main(>{
Graph1 G(10>。

int x,y,w。

int node。

cout<<"你好,请问你向图添加几个节点?几条边?请依次从键盘输入!"<<endl。

int n。

cin>>n。

int e。

cin>>e。

G.CreatG(n,e>。

cout<<"恭喜你!你的图已经建立成功!"<<endl。

//system("pause">。

while(true>
{
//system("cls">。

cout<<"***********************"<<endl。

cout<<"*请选择你要进行的操作:*"<<endl。

cout<<"*1--输出图! *"<<endl。

cout<<"*2--判断图是否连通! *"<<endl。

cout<<"*3--表示深度优先搜索! *"<<endl。

cout<<"*4--表示求各顶点的度! *"<<endl。

cout<<"*5--表示插入新节点!*"<<endl。

cout<<"*6--表示删除边! *"<<endl。

cout<<"*7--求边的权值 *"<<endl。

cout<<"*8--插入新的边! *"<<endl。

cout<<"*0--表示结束操作! *"<<endl。

cout<<"***********************"<<endl。

int choice。

cout<<endl。

cout<<"请你做出选择!"<<endl。

cin>>choice。

switch(choice>
{
case 1:
G.PrintOut(G>。

break。

case 2:
if(G.isliantong(>>
cout<<"该图是连通的!"<<endl。

else cout<<"该图不是连通的!"<<endl。

break。

case 3:
int node。

cout<<"请输入你选择的起始节点!"<<endl。

cin>>node。

cout<<"深度优先搜索结果为:"<<endl。

G.Depth(node-1>。

cout<<endl。

break。

case 4:
G.outDu(G>。

break。

case 5:
cout<<"请输入你要插入的新节点!"<<endl。

cin>>node。

G.insertNode(node>。

cout<<endl。

break。

case 6:
cout<<"请输入你要删除的边!"<<endl。

cin>>x>>y。

G.deleteEdge(x,y>。

cout<<endl。

break。

case 7:
cout<<"请输入你要查询的边的两个顶点"<<endl。

cin>>x>>y。

cout<<G.getWeight(x,y><<endl。

break。

case 8:
cout<<"请输入你要添加的"<<e<<"条边以及边上对应的权值!"<<endl。

cin>>x>>y>>w。

G.isertEdge(x,y,w>。

break。

case 0:
cout<<"感谢使用!"<<endl。

return 0。

}
//system("pause">。

}4 调试分析
4.1 测试数据
测试数据详见图1
4.2调试问题
具体功能方面,在遍历函数中,由于访问节点数组visit[]构建问题,无法达到遍历目的,后新增另一遍历功能函数,用于构建visit[],问题才得以解决,而于使用了清屏system("cls">和暂停system("pause">功能,在测试时一度出现暂停次数过多的问题,通过在判断结构中加入break后解决,在判断是否连通功能上,由于判断问题迟迟未能下手,后在遍历函数中加入了一个记录访问节点数的N,从而解决问题。

4.3 算法时间复杂度
图的创建:时间复杂度为O(n>。

深度遍历:时间复杂度为O(n>。

插入顶点和边:当插入的顶点和边为x,y时,若x>=y时间复杂度为O(x>反之为O(y>。

删除顶点和边:当删除的顶点和边为x,y时,若x>=y时间复杂度为O(x>反之为O(y>。

4.4 经验和心得体会
书上提供的定义结构体的方法很好,想不COPY书上的方法,而是改为定义一个结构体,但是重新想想,如果作为一个编程者,并不是一定要全靠自己想方法,如果是有更简便的方法当然好,但是明知道自己的方法更复杂,却还要一条道走到黑,这样太不明智了,我要做的,就是学会如何使用老师的简便方法,并使用于以后的学习中;而在自己编写过程中,虽然经常编译一次性通过,但是在接下来的测试中,基本上每解决一个功能性问题但是却马上接着出现一个新的问题;一次又一次的更改确实严重打击了我的信心和耐心,最后还是不得不承认自己的能力太弱,所以一个简单的函数都解决不了,希望自己在以后的学习中能有质的飞跃!
5 用户使用说明
本系统是关于图的矩阵存储系统,管理员和游客,要求首先创建一个图之后才能进行后续的操作,并且在输入过程中务必规范输入。

6测试结果
6.1 创建图
图6.1图的创建
6.2插入节点
图6.2节点的插入6.3 深度优先遍历
图6.3深度优先遍历6.4 求各顶点的度
图6.4定点的度6.5 输出图
图6.5图的输出6.6 判断是否连通
图6.6判断是否连通6.7 求边的权值
图6.7边的权值6.8 插入边
个人资料整理仅限学习使用
图6.8边的插入
6.9 删除边
图6.9边的删除
结论
本次课程设计“无向图的邻接矩阵存储结构”按照任务书相应的要求成功的完成了任务,由于本课程设计涉及任务明确,采用图的储存结构和算法比较方便处理数据的储存、查询、删除等操作。

但图的操作比较难,容易出错并且不易改动。

相关文档
最新文档