-
实验目的
熟悉图的数组表示法和邻接表存储结构,掌握构造有向图、无向图的算法 ,在掌握以上知识的基础上,熟悉图的深度优先遍历算法,并实现。
-
实验内容
(1)图的数组表示法定义及基本操作的实现。
(2)图的邻接表表示法定义及基本操作的实现。
(3)写函数实现图的深度优先遍历(分别在两种结构上)
(4)在邻接表上实现拓扑排序、关键路径的求法,在邻接矩阵上实现最短路经、最小生成树的求法。
-
问题描述
(说明你选做的题目及要求)
(2)图的邻接表表示法定义及基本操作的实现。邻接表(Adjacency List)是图的一种顺序存储与链式存储结合的存储方法。邻接表表示法类似于树的孩子链表表示法。就是对于图G 中的每个顶点vi,将所有邻接于vi 的顶点vj 链成一个单链表,这个单链表就称为顶点vi 的邻接表,再将所有点的邻接表表头放到数组中,就构成了图的邻接表
-
数据结构定义
(说明你算法中用到的数据结构、数据类型的定义)
一个图的邻接表存储结构可形式地说明如下:
#define MAX_VERTEX_NUM 20
Typedef struct ArcNode {
Int adjvex; //该弧所指向的顶点的位置
Struct ArcNode *nexttarc; //指向下一条弧的指针
InfoType *info; //该弧相关信息的指针
}ArcNode ;
typedef struct VNode{
VertexType data; //顶点信息
ArcNode *firstarc; //指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_ NUM];
Typedef struct{
AdjList vertices;
Int vexnum,arcnum;//图的当前顶点数和弧数
Int kind; //图的种类标志
}ALGraph;
-
算法思想及算法设计
(先文字说明算法的思想,然后给出类C语言算法)
图的邻接表表示法类似于树的孩子链表表示法。对于图G中的每个顶点vi,该方法把所有邻接于vi的顶点vj链成一个带头结点的单链表,这个单链表就称为顶点vi的邻接表.建立顶点表读入顶点信息边表头指针置为空读入边(Vi, Vj)的顶点对应的序号生成边表节点
void CreatALGraph(ALGraph G)
{
int i, j, k, a;
EdgeNode s; //定义边表节点
printf("请输入顶点数和边数:\n");
scanf("%d %d", &i, &j);
G->n = i;
G->e = j;
printf("请输入顶点编号:\n");
for (i = 1; i <= G->n; i++) //建立顶点表
{
scanf("%d", &a);
G->adjlist[i].vertex = a; //读入顶点信息
G->adjlist[i].firstedge = NULL; //边表头指针置为空
}
printf("请输入由两个定点构成的边,示例:0 1\n");
for (k = 0; k < G->e; k++)
{
scanf("%d %d", &i, &j); //读入边(Vi, Vj)的顶点对应的序号
s = malloc(sizeof(struct node)); //生成边表节点
s->adjvex = j;
s->next = G->adjlist[i].firstedge;
G->adjlist[i].firstedge = s; //将新节点*s插入顶点Vi的边表头部
}
}
-
实验代码
(即C语言程序)
#include <stdio.h>
#include <stdlib.h>
#define MaxVertexNum 50
typedef struct node *EdgeNode;
typedef struct vnode *VertexNode;
typedef struct graph *ALGraph;
struct node
{ //边表节点
int adjvex; //邻接点域
EdgeNode next; //链域
};
struct vnode
{ //顶点表节点
int vertex; //顶点域
EdgeNode firstedge; //边表头指针
};
struct graph
{
struct vnode *adjlist;
int n; //图中当前顶点数
int e; //图中当前边数
};
void CreatALGraph(ALGraph G)
{
int i, j, k;
int a;
EdgeNode s; //定义边表节点
printf("请输入顶点数和边数:\n");
scanf("%d %d", &i, &j);
G->n = i;
G->e = j;
printf("请输入顶点编号:\n");
for (i = 1; i <= G->n; i++) //建立顶点表
{
scanf("%d", &a);
G->adjlist[i].vertex = a; //读入顶点信息
G->adjlist[i].firstedge = NULL; //边表头指针置为空
}
printf("请输入由两个定点构成的边,示例:0 1\n");
for (k = 0; k < G->e; k++)
{
scanf("%d %d", &i, &j); //读入边(Vi, Vj)的顶点对应的序号
s = malloc(sizeof(struct node)); //生成边表节点
s->adjvex = j;
s->next = G->adjlist[i].firstedge;
G->adjlist[i].firstedge = s; //将新节点*s插入顶点Vi的边表头部
}
}
int main()
{
int i, j;
ALGraph G = malloc(sizeof(struct graph));
G->adjlist = (struct vnode *)malloc(sizeof(struct vnode) * MaxVertexNum);
CreatALGraph(G);
for (i = 1; i <= G->n; i++)
{
while (G->adjlist[i].firstedge)
{
printf("%d -> ", G->adjlist[i].vertex);
printf("%d\n", G->adjlist[i].firstedge->adjvex);
G->adjlist[i].firstedge = G->adjlist[i].firstedge->next;
}
}
return 0;
}
-
算法测试结果
(说明测试数据,粘贴实验结果图)
-
分析与总结
(1)算法复杂度分析及优、缺点分析
(说明你编写算法的复杂度,算法的优点和缺点有哪些)
设图中有n个顶点,e条边,则用邻接表表示无向图时,需要n个顶点结点,2e个表结点;用邻接表表示有向图时,若不考虑逆邻接表,只需n个顶点结点,e个边结点。在无向图的邻接表中,顶点vi的度恰为第i个链表中的结点数。在有向图中,第i个链表中的结点个数只是顶点vi的出度。在逆邻接表中的第i个链表中的结点个数为vi的入度。建立邻接表的时间复杂度为O(n+e)
(2)实验总结
(说明你怎么解决实验中遇到的问题,有什么收获)
通过这次实验让我熟悉图的数组表示法和邻接表存储结构,掌握构造有向图、无向图的算法 ,在掌握以上知识的基础上,熟悉图的深度优先遍历算法,并实现基本掌握邻接表的存储结构以及邻接表的建立和操作,但对构建邻接表的算法理解还不够,实验存在一些问题。在邻接表上容易找到任一顶点的一个邻接点和下一个邻接点,但要判断两个顶点(vi和vj)之间是否有边或弧相连,则需搜索需第i个或第j个链表,不及邻接矩阵方便。