求一个无向图G地连通分量地个数.docx
《求一个无向图G地连通分量地个数.docx》由会员分享,可在线阅读,更多相关《求一个无向图G地连通分量地个数.docx(16页珍藏版)》请在冰豆网上搜索。
求一个无向图G地连通分量地个数
《数据结构》实验报告
实验内容:
(一)判断一个图有无回路
(二)求一个无向图G的连通分量的个数
一、目的和要求(需求分析):
1、了解图的定义和图的存储结构。
2、熟悉掌握图的邻接矩阵和邻接表。
3、理解图的遍历算法---深度优先搜索和广度优先搜索。
4、学会编程处理图的连通性问题。
二、程序设计的基本思想,原理和算法描述:
(包括程序的结构,数据结构,输入/输出设计,符号名说明等)
判断一个图有无回路:
在程序设计中,先必须确定所要创建的图是有向还是无向,是图还是网,其次再根据各自的特点,用连接表来实现创建。
在有向图中,先找出入度为0的顶点,删除与这个顶点相关联的边(出边),将与这些边相关的其它顶点的入度减1,循环直到没有入度为0的顶点。
如果此时还有未被删除的顶点,则必然存在环路,否则不存在回路。
无向图则可以转化为:
如果存在回路,则必然存在一个子图,是一个回路。
因此回路中所有定点的度>=2。
第一步:
删除所有度<=1的顶点及相关边,并将另外与这些边相关的其它顶点的度减1。
第二步:
将度数变为1的顶点排入队列,并从该队列中(使用栈)取出一个顶点,并重复步骤一。
如果最后还有未删除的顶点,则存在回路,否则没有。
求一个无向图G的连通分量的个数:
用连接表创建图,对于非连通图,则需从多个顶点出发进行搜索,而每一次从一个新的起始点出发进行搜索过程中得到的顶点访问序列恰为其各个连通分量中的顶点集。
所以在设计中,为了统计出无向图中的连通分量个数,则因在其深度优先所搜无向图时对函数DFSTraverse(ALGraphG)调用DFS次数进行统计,其结果便为无向图中连通分量个数。
三、调试和运行程序过程中产生的问题及采取的措施:
在调试和运行求一个无向图G的连通分量的个数程序时,由于执行语句块
voidDFSTraverse(ALGraphG)先于voidDFS(ALGraphG,intv),
而voidDFSTraverse(ALGraphG)内调用了DFS(),因此计算机无法正确运行,将两者顺序进行了交换,程序便实现了其功能,且运行正常。
四、源程序及注释:
判断一个图有无回路:
#include
#include
#include
//输出有向图的一个拓扑序列。
//图的邻接表存储表示
#defineMAX_NAME3//顶点字符串的最大长度+1
#defineMAX_VERTEX_NUM20
#defineSTACK_INIT_SIZE10//存储空间初始分配量
#defineSTACKINCREMENT2//存储空间分配增量
typedefintInfoType;//存放网的权值
typedefcharVertexType[MAX_NAME];//字符串类型
typedefenum{DG,DN,AG,AN}GraphKind;//{有向图,有向网,无向图,无向网}
typedefstructArcNode
{
intadjvex;//该弧所指向的顶点的位置
structArcNode*nextarc;//指向下一条弧的指针
InfoType*info;//网的权值指针)
}ArcNode;//表结点
typedefstructVNode
{
VertexTypedata;//顶点信息
ArcNode*firstarc;//第一个表结点的地址,指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM];//头结点
typedefstruct
{
AdjListvertices;
intvexnum,arcnum;//图的当前顶点数和弧数
intkind;//图的种类标志
}ALGraph;
//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1。
intLocateVex(ALGraphG,VertexTypeu)
{
inti;
for(i=0;iif(strcmp(u,G.vertices[i].data)==0)
returni;
return-1;
}
//采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图)。
intCreateGraph(ALGraph*G)
{
inti,j,k;
intw;//权值
VertexTypeva,vb;
ArcNode*p;
printf("请输入图的类型(有向图:
0,有向网:
1,无向图:
2,无向网:
3):
");
scanf("%d",&(*G).kind);
printf("请输入图的顶点数和边数:
(空格)\n");
scanf("%d%d",&(*G).vexnum,&(*G).arcnum);
printf("请输入%d个顶点的值(小于%d个字符):
\n",(*G).vexnum,MAX_NAME);
for(i=0;i<(*G).vexnum;++i)//构造顶点向量
{
scanf("%s",(*G).vertices[i].data);
(*G).vertices[i].firstarc=NULL;
}
if((*G).kind==1||(*G).kind==3)//网
printf("请顺序输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):
\n");
else//图
printf("请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):
\n");
for(k=0;k<(*G).arcnum;++k)//构造表结点链表
{
if((*G).kind==1||(*G).kind==3)//网
scanf("%d%s%s",&w,va,vb);
else//图
scanf("%s%s",va,vb);
i=LocateVex(*G,va);//弧尾
j=LocateVex(*G,vb);//弧头
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
if((*G).kind==1||(*G).kind==3)//网
{
p->info=(int*)malloc(sizeof(int));
*(p->info)=w;
}
else
p->info=NULL;//图
p->nextarc=(*G).vertices[i].firstarc;//插在表头
(*G).vertices[i].firstarc=p;
if((*G).kind>=2)//无向图或网,产生第二个表结点
{
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;
if((*G).kind==3)//无向网
{
p->info=(int*)malloc(sizeof(int));
*(p->info)=w;
}
else
p->info=NULL;//无向图
p->nextarc=(*G).vertices[j].firstarc;//插在表头
(*G).vertices[j].firstarc=p;
}
}
return1;
}
voidDisplay(ALGraphG)//输出图的邻接表G。
{
inti;
ArcNode*p;
switch(G.kind)
{
caseDG:
printf("有向图\n");
break;
caseDN:
printf("有向网\n");
break;
caseAG:
printf("无向图\n");
break;
caseAN:
printf("无向网\n");
}
printf("%d个顶点:
\n",G.vexnum);
for(i=0;iprintf("%s",G.vertices[i].data);
printf("\n%d条弧(边):
\n",G.arcnum);
for(i=0;i{
p=G.vertices[i].firstarc;
while(p)
{
if(G.kind<=1)//有向
{
printf("%s→%s",G.vertices[i].data,
G.vertices[p->adjvex].data);
if(G.kind==DN)//网
printf(":
%d",*(p->info));
}
else//无向(避免输出两次)
{
if(iadjvex)
{
printf("%s-%s",G.vertices[i].data,
G.vertices[p->adjvex].data);
if(G.kind==AN)//网
printf(":
%d",*(p->info));
}
}
p=p->nextarc;
}
printf("\n");
}
}
voidFindInDegree(ALGraphG,intindegree[])//求顶点的入度。
{
inti;
ArcNode*p;
for(i=0;iindegree[i]=0;//赋初值
for(i=0;i{
p=G.vertices[i].firstarc;
while(p)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
typedefintSElemType;//栈类型
typedefstructSqStack//栈的顺序存储表示
{
SElemType*base;//在栈构造之前和销毁之后,base的值为NULL
SElemType*top;//栈顶指针
intstacksize;//当前已分配的存储空间,以元素为单位
}SqStack;//顺序栈
intInitStack(SqStack*S)//构造一个空栈S
{
//为栈底分配一个指定大小的存储空间
(*S).base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!
(*S).base)
exit(0);//存储分配失败
(*S).top=(*S).base;//栈底与栈顶相同表示一个空栈
(*S).stacksize=STACK_INIT_SIZE;
return1;
}
//若栈S为空栈(栈顶与栈底相同的),则返回1,否则返回0。
intStackEmpty(SqStackS)
{
if(S.top==S.base)
return1;
else
return0;
}
intPush(SqStack*S,SElemTypee)//插入元素e为新的栈顶元素。
{
if((*S).top-(*S).base>=(*S).stacksize)//栈满,追加存储空间
{
(*S).base=(SElemType*)realloc((*S).base,
((*S).stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!
(*S).base)
exit(0);//存储分配失败
(*S).top=(*S).base+(*S).stacksize;
(*S).stacksize+=STACKINCREMENT;
}
*((*S).top)++=e;
//这个等式的++*优先级相同,但是它们的运算方式,是自右向左
return1;
}
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回1;否则返回0。
intPop(SqStack*S,SElemType*e)
{
if((*S).top==(*S).base)
return0;
*e=*--(*S).top;
//这个等式的++*优先级相同,但是它们的运算方式,是自右向左
return1;
}
//有向图G采用邻接表存储结构。
若G无回路,则输出G的顶点的一个拓扑序
//列并返回1,否则返回0。
intTopologicalSort(ALGraphG)
{
inti,k,count,indegree[MAX_VERTEX_NUM];
SqStackS;
ArcNode*p;
FindInDegree(G,indegree);//对各顶点求入度indegree[0..vernum-1]
InitStack(&S);//初始化栈
for(i=0;iif(!
indegree[i])
Push(&S,i);//入度为0者进栈
count=0;//对输出顶点计数
while(!
StackEmpty(S))
{
//栈不空
Pop(&S,&i);
printf("%s",G.vertices[i].data);//输出i号顶点并计数
++count;
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
{
//对i号顶点的每个邻接点的入度减1
k=p->adjvex;
if(!
(--indegree[k]))//若入度减为0,则入栈
Push(&S,k);
}
}
if(count{
printf("此有向图有回路\n");
return0;
}
else
{
printf("无回路,此图的拓扑序\n");
return1;
}
}
intmain()
{
ALGraphf;
printf("请选择有向图\n");
CreateGraph(&f);
Display(f);
TopologicalSort(f);
system("pause");
return0;
}
求一个无向图G的连通分量的个数
#include
#include
#include
//无向图的邻接表存储表示
#defineMAX_NAME3//顶点字符串的最大长度+1
#defineMAX_VERTEX_NUM20
#defineTRUE1
#defineFALSE0
intvisited[MAX_VERTEX_NUM];//访问标志数组
typedefcharVertexType[MAX_NAME];//字符串类型
typedefstructArcNode
{
intadjvex;//该弧所指向的顶点的位置
structArcNode*nextarc;//指向下一条弧的指针
}ArcNode;//表结点
typedefstructVNode
{
VertexTypedata;//顶点信息
ArcNode*firstarc;//第一个表结点的地址,指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM];//头结点
typedefstruct
{
AdjListvertices;
intvexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;
//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1。
intLocateVex(ALGraphG,VertexTypeu)
{
inti;
for(i=0;iif(strcmp(u,G.vertices[i].data)==0)
returni;
return-1;
}
//采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图)。
intCreateGraph(ALGraph*G)
{
inti,j,k;
VertexTypeva,vb;
ArcNode*p;
printf("请输入无向图的顶点数和边数:
(空格)\n");
scanf("%d%d",&(*G).vexnum,&(*G).arcnum);
printf("请输入%d个顶点的值(小于%d个字符):
\n",(*G).vexnum,MAX_NAME);
for(i=0;i<(*G).vexnum;++i)//构造顶点向量
{
scanf("%s",(*G).vertices[i].data);
(*G).vertices[i].firstarc=NULL;
}
printf("请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):
\n");
for(k=0;k<(*G).arcnum;++k)//构造表结点链表
{
scanf("%s%s",va,vb);
i=LocateVex(*G,va);//弧尾
j=LocateVex(*G,vb);//弧头
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
p->nextarc=(*G).vertices[i].firstarc;//插在表头
(*G).vertices[i].firstarc=p;
//无向图产生第二个表结点
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;
p->nextarc=(*G).vertices[j].firstarc;//插在表头
(*G).vertices[j].firstarc=p;
}
return1;
}
//输出图的邻接表G。
voidDisplay(ALGraphG)
{
inti;
ArcNode*p;
printf("无向图\n");
printf("%d个顶点:
\n",G.vexnum);
for(i=0;iprintf("%s",G.vertices[i].data);
printf("\n%d条弧(边):
\n",G.arcnum);
for(i=0;i{
p=G.vertices[i].firstarc;
while(p)
{
if(iadjvex)//无向(避免输出两次)
{
printf("%s-%s",G.vertices[i].data,
G.vertices[p->adjvex].data);
}
p=p->nextarc;
}
printf("\n");
}
}
voidDFS(ALGraphG,intv)//从第v个顶点出发递归地深度优先遍历图G。
{
ArcNode*p;
visited[v]=TRUE;//访问第v个顶点
for(p=G.vertices[v].firstarc;p;p=p->nextarc)
if(!
visited[p->adjvex])DFS(G,p->adjvex);//对v尚未访问的邻接点递归调用DFS
}
voidDFSTraverse(ALGraphG)//对图G作深度优先遍历。
{
intv,count=0;
for(v=0;vvisited[v]=FALSE;
for(v=0;vif(!
visited[v])
{
DFS(G,v);
count++;//对连通分量进行计数
}
printf("无向图G的连通分量的个数为:
%d",count);
}
intmain()
{
ALGraphf;
CreateGraph(&f);
Display(f);
DFSTraverse(f);
}
五、运行输出结果:
判断一个图有无回路:
请选择有向图
请输入图的类型(有向图:
0,有向网:
1,无向图:
2,无向网:
3):
0
请输入图的顶点数和边数:
(空格)
44
请输入4个顶点的值(小于3个字符):
0
1
2
3
请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):
02
01
23
30
有向图
4个顶点:
0123
4条弧(边):
0→10→2
2→3
3→0
此有向图有回路
请按任意键继续...
求一个无向图G的连通分量的个数
请输入无向图的顶点数和边数:
(空格)
54
请输入5个顶点的值(小于3个字符):
1
2
3
4
5
请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):
12
24
14
35
无向图
5个顶点:
12345
4条弧(边):
1-41-2
2-4
3-5
无向图G的连通分量的个数为:
2
请按任意键继续...
六、心得与体会:
这次上机实验,难度较大,但是经过自己的不懈努力,收获还是颇多的。
首先是对于图的有了更加清晰认识,其次,在程序设计上面也有了很大程度的提高。
对于栈、队等也有了进一步的系统化认