资源描述
资料内容仅供您学习参考,如有不当之处,请联系改正或者删除。
数据结构课程设计
设计题目: 有向图拓扑排序
专 业: 信息与计算科学
学 号:
姓 名: 黄秋实
指导教师: 文 军
11月28日
数据结构课程设计
----拓扑排序
一 需求分析
1.问题描述
本次课程设计题目是: 用邻接表构造图 然后进行拓扑排序, 输出拓扑排序序列
拓扑排序的基本思想为:
1).从有向图中选一个无前驱的顶点输出; 2).将此顶点和以它为起点的弧删除; 3). 重复1),2)直到不存在无前驱的顶点; 4). 若此时输出的顶点数小于有向图中的顶点数, 则说明有向图中存在回路, 否则输出的顶点的顺序即为一个拓扑序列。
2. 拓扑排序 有向图拓朴排序算法的基本步骤如下: ①从图中选择一个入度为0的顶点, 输出该顶点; ②从图中删除该顶点及其相关联的弧, 调整被删弧的弧头结点的入度( 入度-1) ; ③重复执行①、 ②直到所有顶点均被输出, 拓朴排序完成或者图中再也没有入度为0的顶点( 此种情况说明原有向图含有环) 。
3基本要求
(1) 输入的形式和输入值的范围;
首先是输入要排序的顶点数和弧数, 都为整型, 中间用分隔符隔开; 再输入各顶点的值, 为正型, 中间用分隔符隔开; 然后输入各条弧的两个顶点值, 先输入弧头, 再输入弧尾, 中间用分隔符隔开, 输入的值只能是开始输入的顶点值否则系统会提示输入的值的顶点值不正确, 请重新输入, 只要继续输入正确的值就行。
(2) 输出的形式;
首先输出建立的邻接表, 然后是最终各顶点的出度数, 再是拓扑排序的序列, 而且每输出一个顶点, 就会输出一次各顶点的入度数。
(3) 程序所能达到的功能;
因为该程序是求拓扑排序, 因此算法的功能就是要输出拓扑排序的序列,
在一个有向图中, 若用顶点表示活动, 有向边就表示活动间先后顺序, 那么输出的拓扑序列就表示各顶点间的关系为反映出各点的存储结构, 以邻接表存储并输出各顶点的入度。
二 概要设计
1. 算法中用到的所有各种数据类型的定义
在该程序中用邻接表作为图的存储结构。首先, 定义表结点和头结点的结构类型, 然后定义图的结构类型。创立图用邻接表存储的函数, 其中根据要求输入图的顶点和边数, 并根据要求设定每条边的起始位置, 构建邻接表依次将顶点插入到邻接表中。
拓扑排序的函数在该函数中首先要对各顶点求入度, 其中要用到求入度的函数, 为了避免重复检测入度为零的顶点, 设置一个辅助栈, 因此要定义顺序栈类型, 以及栈的函数: 入栈, 出栈, 判断栈是否为空。
2.各程序模块之间的层次调用关系
第一部分, void ALGraph *G函数构建图, 用邻接表存储。这个函数没有调用函数。
第二部分, void TopologicalSort(ALGraph *G)输出拓扑排序函数, 这个函数首先调用FindInDegree(G,indegree)对各顶点求入度indegree[0……vernum-1];然后设置了一个辅助栈, 调用InitStack(&S)初始化栈, 在调用Push(&S,i)入度为0者进栈, while(!StackEmpty(&S))栈不为空时, 调用Pop(&sS,&n)输出栈中顶点并将以该顶点为起点的边删除, 入度indegree[k]--,当输出某一入度为0的顶点时, 便将它从栈中删除。
第三部分, 主函数, 先后调用void CreatGraph(ALGraph *G)函数构建图、
void TopologicalSort(ALGraph *G)函数输出拓扑排序实现整个程序。
3.设计的主程序流程(见附页)
三 详细设计
( 实现概要设计中定义的所有数据类型,对每个操作写出伪码算法; 对主程序和其它模块也都需要写出伪码算法(伪码算法达到的详细程度建议为; 按照伪码算法能够在计算机键盘直接输入高级程序设计语言程序);写出出函数和过程的调用关系。)
1.实现概要设计中定义的所有数据类型
#include<stdio.h>
#include<stdlib.h>
#define MAX_VEXTEX_NUM 100
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define M 100
#define ERROR 0
typedef int ElemType;
typedef struct ArcNode
{
int adjvex;
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;
2.算法和各模块的代码
程序中各函数算法思想如下:
2.1 void InitStack(SqStack *S)
初始化栈将栈的空间设为 STACK-INIT-SIZE。
2.2 int Pop(SqStack *S,ElemType *e)
出栈操作, 若站不空, 删除S的栈顶元素, 用e返回其值, 并返回OK; 否则返回ERROR。
2.3 void Push(SqStack *S,ElemType e)
进栈操作, 插入元素e为新的栈顶元素。
2.4 int StackEmpty(SqStack *S)
判断栈是否为空,语句if (S->top=S->base )判断, 若栈不为空, 则删除S的栈顶元素, 并返回OK; 否则返回ERROR。
2.5 void CreatGraph (ALGraph *G)
构建图, 用邻接表存储, 首先定义邻接表指针变量, 输入顶点数和弧数, 初始化邻接表, 将表头向量域置空, 输入存在弧的点集合, 当输入顶点值超出输入值的范围就会出错, 否则依次插入进邻接表, 最后输出建立好的邻接表。
2.6 void FindInDegree(ALGrap G, int indegreee[])
求入度操作, 设一个存放各顶点入度的数组indegreee[], 然后
indegreee[i]=0赋初值, for循环indegreee[]++, 存储入度数。
2.7 void TopologicalISort(ALGraph G)
输出拓扑排序函数。其思路是若G无回路, 则输出G的顶点的一个拓扑序列并返回OK, 否则返回ERROR。首先由于邻接表的存储结构入度为零的顶点即为没有前驱的顶点, 我们能够附设一个存放个顶点入度的数组, 调用FindInDegree( G, indegreee[])对各顶点求入度; 为了避免重复检测入度为零0的顶点, 设置一个栈, 调用InitStack(&S)初始化栈, 在调用Push(&S,i)入度为0者进栈, while(!StackEmpty(&S))栈不为空时, 调用Pop(&sS,&n)输出栈中顶点并将以该顶点为起点的边删除, 入度indegree[k]--,当输出某一入度为0的顶点时, 便将它从栈中删除。
3.算法的时间复杂度和空间复杂度
拓扑排序实际是对邻接表表示的图G进行遍历的过程, 每次访问一个入度为零的顶点, 若图G中没有回路, 则需扫描邻接表中的所有边结点, 在算法开始时, 为建立入度数组D需访问表头向量中的所有边结点, 算法的时间复杂度为O(n+e)。
四 测试与分析
对有向无环图【下图】进行拓扑排序。
输入:
结果如下:
五 总结
拓扑排序就是对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序, 是将G中所有顶点排成一个线性序列, 使得图中任意一对顶点u和v, 若<u, v> ∈E(G), 则u在线性序列中出现在v之前。
在进行课程设计中, 更好的认识了拓扑排序。理清了各个模块之间算法之间的条理。认识了 伪代码( Pseudocode) 是一种算法描述语言。使用伪代码的目的是为了使被描述的算法能够容易地以任何一种编程语言( Pascal, C, Java, etc) 实现。因此, 伪代码必须结构清晰、 代码简单、 可读性好, 而且类似自然语言。 介于自然语言与编程语言之间。它是一种让人便于理解的代码。不依赖于语言的, 用来表示程序执行过程, 而不一定能编译运行的代码。在数据结构讲算法的时候用的很多。
在设计中, 我们遇到了程序正确, 却对某些无向图无法进行拓扑排序的问题。多次对程序进行修改后, 才能够进行拓扑排序。问题出在调用函数的错误理解, 模块之间的联系模糊不清。
六附录: 源程序:
#include<stdio.h>
#include<stdlib.h>
#define MAX_VEXTEX_NUM 100
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define M 100
#define ERROR 0
typedef int ElemType;
typedef struct ArcNode
{
int adjvex;
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("\n请输入存在边的两个顶点的序号, 先输入弧尾, 再输入弧头:");
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,b,j=0;
int a[20];
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);
}
while(!StackEmpty(&S))
{
Pop(&S,&n);
a[j]=G.vertices[n].data;
j++;
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("该有向图有环\n");
}
else
{
printf("排序成功\n");
printf("进行拓扑排序输出顺序为:");
for (b=0;b<j;b++)
{
printf("%4d",a[b]);
}
printf("\n");
}
}
int main(void)
{
ALGraph G;
CreatGraph(&G);
TopologicalSort(G);
system("pause");
return 0;
}
展开阅读全文