cin>>n;
BFS(ALG,n);
}
break;
default:
if(n!
=5)
cout<<"错误,重新输入\n";
}
}while(n!
=5);
}
voidUDN()
{
MGraphMG;
ALGraphALG;
intn;
do{
cout<<"\n";
cout<<"***************无向网的基本操作及应用***************\n";
cout<<"*1创建无向网的邻接矩阵*\n";
cout<<"*2创建无向网的邻接表*\n";
cout<<"*3prim算法求最小生成树*\n";
cout<<"*4kraskal算法求最小生成树*\n";
cout<<"*5退出*\n";
cout<<"****************************************************\n";
cin>>n;
switch(n){
case1:
CreatUNG_M(MG);
break;
case2:
{
CreatUNG_ALG(ALG);
dispgraph(ALG);
}
break;
case3:
{
CreatUNG_M(MG);
cout<<"起始出发顶点为:
";
cin>>n;
Prim(MG,n);
}
break;
case4:
{
CreatUNG_M(MG);
Kruskal(MG);
}
break;
default:
if(n!
=5)
cout<<"错误,重新输入\n";
}
}while(n!
=5);
}
voidDG()
{
MGraphMG;
ALGraphALG;
intn;
do
{
cout<<"\n";
cout<<"***************有向图的基本操作及应用***************\n";
cout<<"*1创建有向图的邻接矩阵*\n";
cout<<"*2创建有向图的邻接表*\n";
cout<<"*3拓扑排序*\n";
cout<<"*4退出*\n";
cout<<"****************************************************\n";
cin>>n;
switch(n){
case1:
CreatDG_M(MG);
break;
case2:
{
CreatDG_ALG(ALG);
dispgraph(ALG);
}
break;
case3:
{
CreatDG_ALG(ALG);
TopoSort(ALG);
}
break;
default:
if(n!
=4)
cout<<"错误,重新输入\n";
}
}while(n!
=4);
}
voidDN()
{
MGraphMG;
ALGraphALG;
intn;
do{
cout<<"\n";
cout<<"***************有向网的基本操作及应用***************\n";
cout<<"*1创建有向网的邻接矩阵*\n";
cout<<"*2创建有向网的邻接表*\n";
cout<<"*3关键路径*\n";
cout<<"*4单源顶点最短路径问题*\n";
cout<<"*5每对顶点最短路径问题*\n";
cout<<"*6退出*\n";
cout<<"****************************************************\n";
cin>>n;
switch(n)
{
case1:
CreatDNG_M(MG);
break;
case2:
{
CreatDNG_ALG(ALG);
dispgraph(ALG);
}
break;
case3:
{
CreatDNG_ALG(ALG);
KeyPath(ALG);
}
break;
case4:
{
CreatDNG_M(MG);
cout<<"起始出发顶点为:
";
cin>>n;
SinMiniPathD(MG,n);
}
break;
case5:
{
CreatDNG_M(MG);
DouMiniPathF(MG);
}
break;
default:
if(n!
=6)
cout<<"错误,重新输入\n";
}
}while(n!
=6);
}
voidmain()
{
intn;
do{
ShowMainMenu();
cin>>n;
switch(n){
case1:
UDG();
break;
case2:
UDN();
break;
case3:
DG();
break;
case4:
DN();
break;
default:
if(n!
=5)
cout<<"错误,重新输入\n";
}
}while(n!
=5);
}
图是由若干个顶点和若干条边构成的结构,每个顶点具有任意多个前驱和后继。
顶点是一些具体对象的抽象,而边是对象间关系的抽象。
在具体应用中,顶点和边被赋予具体的含义。
如一个交通图中,用顶点代表城市,边表示城市之间的交通联系。
其形式化定义为:
G=(V,E)
V={vi|vi∈dataobject}
E={(vi,vj)|vi,vj∈V∧P(vi,vj)}
图是一种结构复杂的数据结构,其信息包括两部分:
图中数据元素即顶点的信息,元素间的关系(顶点之间的关系)——边或者弧的信息。
无论采用爰方法建立图的存储结构,都要完整、准确地反映这两方面的信息。
一般情况下图有四种存储结构:
邻接矩阵存储、邻接表存储、十字链表存储、邻接多重表存储。
此次仅讨论前两种存储结构。
二、无向图的基本操作与应用
无向图的基本操作与应用包括用邻接矩阵和邻接表构造无向图以及以邻接表为存储结构的无向图的深度优先遍历和广度优先遍历。
2.1、设计方案
无向图可以用邻接矩阵、邻接表两种存储结构来表示,用一维数组存储图的顶点的信息,用二维数组存储图中边或弧的信息,该二维数组称为邻接矩阵。
设G=(V,E)为具有n个顶点的图,若用二维数组A表示图的邻接矩阵,则A是一个n阶的方阵,其中:
A[i][j]=
邻接矩阵的存储结构定义如下:
#defineMAXVEX30/*最大顶点数设为20*/
typedefcharVertexType;/*顶点类型设为字符型*/
typedefstruct
{
VertexTypevexs[MAXVEX];/*顶点表*/
intarcs[MAXVEX][MAXVEX];/*邻接矩阵*/
intvexnum,arcnum;/*图中顶点数和边数*/
}MGraph;/*邻接矩阵存储的图类型*/
无向图的邻接矩阵是一个对称矩阵,其第i行(或第i列)非零元素的个数为第i个顶点的度TD(vi)。
有向图的邻接矩阵的第i行非零元素的个数正好为第i个顶点的出度OD(vi),第i列非零元素的个数为第i个顶点的入度ID(vi)。
邻接表表示法类似于树的孩子链表表示法。
它对图中的每个顶点vi建立一个带头结点的线性链表,用于存储图中与vi相邻接的边或信息。
头结点中存放该顶点的信息。
所有头结点用一个顺序表存放。
其中,头结点由两个域组成:
存储顶点信息的data域和指向链表中第一个结点的指针域firstarc。
表结点由三个域组成:
邻接点域dajvex、指向下一条边或弧的指针域nextarc和存储边或弧上相关信息(如权值等)的域info。
邻接表表示远射图,每条边(vi,vj)在两个顶点vi,vj的链表中各占一个表节点。
因此,每条边在链表中存储两次。
若无向图中有n个顶点、e条边,则它的邻接表需n个头结点和2e个表结点。
顶点vi的度恰为第i个链表中的结点数。
邻接表存储结构的定义如下:
typedefstructArcNode{
intadjvex;//邻接点序号
intw;//边或狐上的权
structArcNode*next;
}ArcNode;
typedefstructvnode{
VertexTypedata;//顶点信息
intindegree;
ArcNode*firstarc;//指向下一个边结点
}Vnode,AdjList[MAXVEX];
typedefstruct{
AdjListvertices;
intvexnum,arcnum;
}ALGraph;
邻接表存储图很容易找到任一顶点的邻接点,但要判定任意两个顶点(vi和vj)之间是否有边或弧相连,则需搜索第i个或第j个链表,不及邻接矩阵方便。
设图G有n个顶点,e条边,图的邻接矩阵表示的空间代价是O(n2),只与图的顶点数有关。
若图G是无向图,图的邻接表表示的空间代价是O(n+2e);若图G是有向图,图的邻接表表示的空间代价是O(n+e)。
2.2、实现过程
键盘输入图的顶点数和边数,用循环来实现顶点的数组G.vexs[i],用同样的方法构造出邻接矩阵。
两点这有边的赋值为1,否则为0。
用邻接矩阵创建无向图的函数为CreatUDG_M(MGraph&G),用邻接表创建无向图的函数为CreatUDG_ALG(ALGraph&G)。
其代码参见头文件UDGraph.h。
给定一个图G,我们希望从图中的某个顶点出发,经过一定的路线访问图中所有可访问的顶点,使每个可访问到的顶点都被访问且只被访问一次。
这一过程称为图的遍历。
图的遍历是图的一种基本操作,图的许多其他操作都是建立在遍历的基础上的。
由于图结构本身的复杂性,图的遍历操作也较复杂。
在图结构中,如果有回路存在,那么一个顶点被访问之后,有可能沿回路又回到该顶点上。
为了避免同一顶点被访问多次,当顶点vi被访问时,对应的数组元素置1,否则置0。
图的遍历通常有深度优先遍历和广度优先遍历两种方式。
2.2.1尝试优先遍历
深度优先遍历类似于树的先根遍历,是树的先根遍历的推广。
其具体思想是:
从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直到图中所有和v有路径相通的顶点都被访问到。
若此时图中还有顶点未被访问,另选图中一个未曾被访问的顶点作起点,重复上述过程,直到图中所有顶点都被访问到为止。
这是一个递归的过程。
以邻接表为图的存储结构的深度优先遍历算法如下:
intvisited[MAXVEX]={0};
voidDFS(ALGraphG,intv)
{
ArcNode*p;
visited[v]=1;
printf("[%d,%c]",v,G.vertices[v].data);
p=G.vertices[v].firstarc;
while(p)
{
if(!
visited[p->adjvex])
DFS(G,p->adjvex);
p=p->next;
}
}算法运行结果如下图所示:
在遍历时,对图中每个顶点至多访问一次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。
因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。
其耗费的时间则取决于所采用的存储结构。
对于n个顶点,e条边或弧的图,当用邻接矩阵作其存储结构时,深度遍历图的时间复杂度为O(n2)。
而当以邻接表作其存储结构时,深度优先遍历图的时间复杂度为O(n+e)。
2.2.2广度倚靠遍历
广度优先遍历类似于树的层次遍历。
其基本思想是:
从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问未被访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直到图中所有已被访问的顶点的邻接点都被访问到。
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起点,重复上述过程,直到图中所有顶点都被访问到为止。
以邻接表作为图的存储结构的广度优先遍历算法如下:
voidBFS(ALGraphG,intv)
{
LinkQueueQ;
intvi;
ArcNode*p;
InitQueue(Q);
visited[v]=1;
printf("[%d,%c]",v,G.vertices[v].data);
EnQueue(Q,v);
while(!
QueueEmpty(Q))
{
EnQueue(Q,vi);
p=G.vertices[vi].firstarc;
while(p)
{
if(visited[p->adjvex]!
=1)
{
visited[p->adjvex]=1;
printf("[%d,%c]",p->adjvex,G.vertices[p->adjvex].data);
EnQueue(Q,p->adjvex);
}
p=p->next;
}
}
}算法运行结果如下图所示:
2.2.3代码实现
*********************************UDGraph.h**********************************
#include"LinkQueue.h"
voidCreatUDG_M(MGraph&G)
{
inti,j,c;
cout<<"请输入顶点数,边数:
";
cin>>G.vexnum>>G.arcnum;
printf("请输入顶点:
");
for(i=1;i<=G.vexnum;i++)
cin>>G.vexs[i];
for(i=1;i<=G.vexnum;i++)
for(j=1;j<=G.vexnum;j++)
G.arcs[i][j]=0;
printf("请输入边:
\n");
for(c=1;c<=G.arcnum;c++)
{
cin>>i>>j;
G.arcs[i][j]=1;
G.arcs[j][i]=1;
}
cout<<"创建的邻接矩阵为:
"<for(i=1;i<=G.vexnum;i++)
{
for(j=1;j<=G.vexnum;j++)
cout<cout<}
cout<<"邻接矩阵创建完毕"<}算法运行结果如下图所示:
voidCreatUDG_ALG(ALGraph&G)
{
inti,s,d;
ArcNode*p,*q;
cout<<"请输入顶点数,边数:
";
cin>>G.vexnum>>G.arcnum;
for(i=1;i<=G.vexnum;i++)
{
printf("第%d个结点信息:
",i);
cin>>G.vertices[i].data;
G.vertices[i].firstarc=NULL;
}
for(i=1;i<=G.arcnum;i++)
{
printf("第%d条边=>起点序号,终点序号:
",i);
cin>>s>>d;
p=(ArcNode*)malloc(sizeof(ArcNode));
q=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=d;
q->adjvex=s;
p->next=G.vertices[s].firstarc;/*p插入顶点s的邻接表中*/
G.vertices[s].firstarc=p;
q->next=G.vertices[d].firstarc;/*q插入顶点d的邻接表中*/
G.vertices[d].firstarc=q;
}
}算法运行结果如下图所示:
voiddispgraph(ALGraphG)
{
inti;
ArcNode*p;
printf("图的邻接表表示如下:
\n");
for(i=1;i<=G.vexnum;i++)
{
printf("[%d,%c]=>",i,G.vertices[i].data);
p=G.vertices[i].firstarc;
while(p!
=NULL)
{
printf("[%d→]",p->adjvex);
p=p->next;
}
printf("∧\n");
}
}
intvisited[MAXVEX]={0};
voidDFS(ALGraphG,intv)
{
ArcNode*p;
visited[v]=1;
printf("[%d,%c]",v,G.vertices[v].data);
p=G.vertices[v].firstarc;
while(p)
{
if(!
visited[p->adjvex])
DFS(G,p->adjvex);
p=p->next;
}
}
voidBFS(ALGraphG,intv)
{
LinkQueueQ;
intvi;
ArcNode*p;
InitQueue(Q);
visited[v]=1;
printf("[%d,%c]",v,G.vertices[v].data);
EnQueue(Q