第7章 图2.docx
《第7章 图2.docx》由会员分享,可在线阅读,更多相关《第7章 图2.docx(38页珍藏版)》请在冰豆网上搜索。
第7章图2
五.算法设计题
1. voidCreatGraph(AdjListg)
//建立有n个顶点和m条边的无向图的邻接表存储结构
{intn,m;
scanf("%d%d",&n,&m);
for(i=1,i<=n;i++)//输入顶点信息,建立顶点向量
{scanf(&g[i].vertex);g[i].firstarc=null;}
for(k=1;k<=m;k++)//输入边信息
{scanf(&v1,&v2);//输入两个顶点
i=GraphLocateVertex(g,v1);j=GraphLocateVertex(g,v2);//顶点定位
p=(ArcNode*)malloc(sizeof(ArcNode));//申请边结点
p->adjvex=j;p->next=g[i].firstarc;g[i].firstarc=p;//将边结点链入
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;p->next=g[j].firstarc;g[j].frstarc=p;
}
}//算法CreatGraph结束
2. voidCreatAdjList(AdjListg)
//建立有向图的邻接表存储结构
{intn;
scanf("%d",&n);
for(i=1;i<=n;j++)
{scanf(&g[i].vertex);g[i].firstarc=null;}//输入顶点信息
scanf(&v1,.&v2);
while(v1&&v2)//题目要求两顶点之一为0表示结束
{i=GraphLocateVertex(g2,v1);
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;p->next=g[i].firstarc;g[i].firstarc=p;
scanf(&v1,&v2);
}}
3. voidCreatMGraph(AdjMulistg)
//建立有n个顶点e条边的无向图的邻接多重表的存储结构
{intn,e;
scanf("%d%d",&n,&e);
for(i=1,i<=n;i++)//建立顶点向量
{scanf(&g[i].vertex);g[i].firstedge=null;}
for(k=1;k<=e;k++)//建立边结点
{scanf(&v1,&v2);
i=GraphLocateVertex(g,v1);j=GraphLocateVertex(g,v2);
p=(ENode*)malloc(sizeof(ENode));
p->ivex=i;p->jvex=j;p->ilink=g[i].firstedge;p->jlink=g[j].firstedge;
g[i].firstedge=p;g[j].firstedge=p;
}
}//算法结束
4. voidCreatOrthList(OrthListg)
//建立有向图的十字链表存储结构
{inti,j,v;//假定权值为整型
scanf("%d",&n);
for(i=1,i<=n;i++)//建立顶点向量
{scanf(&g[i].vertex);g[i].firstin=null;g[i].firstout=null;}
scanf("%d%d%d",&i,&j,&v);
while(i&&j&&v)//当输入i,j,v之一为0时,结束算法运行
{p=(OrArcNode*)malloc(sizeof(OrArcNode));//申请结点
p->headvex=j;p->tailvex=i;p->weight=v;//弧结点中权值域
p->headlink=g[j].firstin;g[j].firstin=p;
p->tailink=g[i].firstout;g[i].firstout=p;
scanf("%d%d%d",&i,&j,&v);
}}算法结束
[算法讨论]本题已假定输入的i和j是顶点号,否则,顶点的信息要输入,且用顶点定位函数求出顶点在顶点向量中的下标。
图建立时,若已知边数(如上面1和2题),可以用for循环;若不知边数,可用while循环(如本题),规定输入特殊数(如本题的零值)时结束运行。
本题中数值设为整型,否则应以和数值类型相容的方式输入。
5.voidInvertAdjList(AdjListgin,gout)
//将有向图的出度邻接表改为按入度建立的逆邻接表
{for(i=1;i<=n;i++)//设有向图有n个顶点,建逆邻接表的顶点向量。
{gin[i].vertex=gout[i].vertex;gin.firstarc=null;}
for(i=1;i<=n;i++)//邻接表转为逆邻接表。
{p=gout[i].firstarc;//取指向邻接表的指针。
while(p!
=null)
{j=p->adjvex;
s=(ArcNode*)malloc(sizeof(ArcNode));//申请结点空间。
s->adjvex=i;s->next=gin[j].firstarc;gin[j].firstarc=s;
p=p->next;//下一个邻接点。
}//while
}//for}
6.voidAdjListToAdjMatrix(AdjListgl,AdjMatrixgm)
//将图的邻接表表示转换为邻接矩阵表示。
{for(i=1;i<=n;i++)//设图有n个顶点,邻接矩阵初始化。
for(j=1;j<=n;j++)gm[i][j]=0;
for(i=1;i<=n;i++)
{p=gl[i].firstarc;//取第一个邻接点。
while(p!
=null){gm[i][p->adjvex]=1;p=p->next;}//下一个邻接点
}//for}//算法结束
7.voidAdjMatrixToAdjList(AdjMatrixgm,AdjListgl)
//将图的邻接矩阵表示法转换为邻接表表示法。
{for(i=1;i<=n;i++)//邻接表表头向量初始化。
{scanf(&gl[i].vertex);gl[i].firstarc=null;}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(gm[i][j]==1)
{p=(ArcNode*)malloc(sizeof(ArcNode));//申请结点空间。
p->adjvex=j;//顶点I的邻接点是j
p->next=gl[i].firstarc;gl[i].firstarc=p;//链入顶点i的邻接点链表中
}
}//end
[算法讨论]算法中邻接表中顶点在向量表中的下标与其在邻接矩阵中的行号相同。
8.[题目分析]在有向图中,判断顶点Vi和顶点Vj间是否有路径,可采用遍历的方法,从顶点Vi出发,不论是深度优先遍历(dfs)还是宽度优先遍历(bfs),在未退出dfs或bfs前,若访问到Vj,则说明有通路,否则无通路。
设一全程变量flag。
初始化为0,若有通路,则flag=1。
算法1:
intvisited[]=0;//全局变量,访问数组初始化
intdfs(AdjListg,vi)
//以邻接表为存储结构的有向图g,判断顶点Vi到Vj是否有通路,返回1或0表示有或无
{visited[vi]=1;//visited是访问数组,设顶点的信息就是顶点编号。
p=g[vi].firstarc;//第一个邻接点。
while(p!
=null)
{j=p->adjvex;
if(vj==j){flag=1;return
(1);}//vi和vj有通路。
if(visited[j]==0)dfs(g,j);
p=p->next;}//while
if(!
flag)return(0);
}//结束
[算法讨论]若顶点vi和vj不是编号,必须先用顶点定位函数,查出其在邻接表顶点向量中的下标i和j。
下面算法2输出vi到vj的路径,其思想是用一个栈存放遍历的顶点,遇到顶点vj时输出路径。
算法2:
voiddfs(AdjListg,inti)
//有向图g的顶点vi(编号i)和顶点vj(编号j)间是否有路径,如有,则输出。
{inttop=0,stack[];//stack是存放顶点编号的栈
visited[i]=1;//visited数组在进入dfs前已初始化。
stack[++top]=i;
p=g[i].firstarc;/求第一个邻接点.
while(p)
{if(p->adjvex==j)
{stack[++top]=j;printf("顶点vi和vj的路径为:
\n");
for(i=1;i<=top;i++)printf("%4d",stack[i]);exit(0);
}//if
elseif(visited[p->adjvex]==0){dfs(g,g->adjvex);top--;p=p->next;}//elseif
}//while
}//结束算法2
算法3:
本题用非递归算法求解。
intConnectij(AdjListg,vertypevi,vj)
//判断n个顶点以邻接表表示的有向图g中,顶点Vi各Vj是否有路径,有则返回1,否则返回0。
{for(i=1;i<=n;i++)visited[i]=0;//访问标记数组初始化。
i=GraphLocateVertex(g,vi);//顶点定位,不考虑vi或vj不在图中的情况。
j=GraphLocateVertex(g,vj);
intstack[],top=0;stack[++top]=i;
while(top>0)
{k=stack[top--];p=g[k].firstarc;
while(p!
=null&&visited[p->adjvex]==1)p=p->next;//查第k个链表中第一个未访问的弧结点。
if(p==null)top--;
else{i=p->adjvex;
if(i==j)return
(1);//顶点vi和vj间有路径。
else{visited[i]=1;stack[++top]=i;}//else
}//else
}while
return(0);}//顶点vi和vj间无通路。
9.voidDeletEdge(AdjListg,inti,j)
//在用邻接表方式存储的无向图g中,删除边(i,j)
{p=g[i].firstarc;pre=null;//删顶点i的边结点(i,j),pre是前驱指针
while(p)
if(p->adjvex==j)
{if(pre==null)g[i].firstarc=p->next;elsepre->next=p->next;free(p);}//释放结点空间。
else{pre=p;p=p->next;}//沿链表继续查找
p=g[j].firstarc;pre=null;//删顶点j的边结点(j,i)
while(p)
if(p->adjvex==i)
{if(pre==null)g[j].firstarc=p->next;elsepre->next=p->next;free(p);}//释放结点空间。
else{pre=p;p=p->next;}//沿链表继续查找
}//DeletEdge
[算法讨论]算法中假定给的i,j均存在,否则应检查其合法性。
若未给顶点编号,而给出顶点信息,则先用顶点定位函数求出其在邻接表顶点向量中的下标i和j。
10.voidDeleteArc(AdjListg,vertypevi,vj)
//删除以邻接表存储的有向图g的一条弧,假定顶点vi和vj存在
{i=GraphLocateVertex(g,vi);j=GraphLocateVertex(g,vj);//顶点定位
p=g[i].firstarc;pre=null;
while(p)
if(p->adjvex==j)
{if(pre==null)g[i].firstarc=p->next;elsepre->next=p->next;free(p);}//释放结点空间。
else{pre=p;p=p->next;}
}//结束
11.voidInsertArc(OrthListg,vertypevi,vj)
//在以十字链表示的有向图g中插入弧
{i=GraphLocateVertex(g,vi);//顶点定位.
j=GraphLocateVertex(g,vj);
p=(OrArcNode*)malloc(sizeof(OrArcNode));
p=headvex=j;p=tailvex=i;//填写弧结点信息并插入十字链表。
p->headlink=g[j].firstin;g[j].firstin=p;
p->taillink=g[i].firstout;g[i].firstout=p;
}//算法结束
12.[题目分析]在有向图的邻接表中,求顶点的出度容易,只要简单在该顶点的邻接点链表中查结点个数即可。
而求顶点的入度,则要遍历整个邻接表。
intcount(AdjListg,intk)
//在n个顶点以邻接表表示的有向图g中,求指定顶点k(1<=k<=n)的入度。
{intcount=0;
for(i=1;i<=n;i++)//求顶点k的入度要遍历整个邻接表。
if(i!
=k)//顶点k的邻接链表不必计算
{p=g[i].firstarc;//取顶点i的邻接表。
while(p)
{if(p->adjvex==k)count++;
p=p->next;
}//while
}//if
return(count);//顶点k的入度.
}
13.[题目分析]有向图判断回路要比无向图复杂。
利用深度优先遍历,将顶点分成三类:
未访问;已访问但其邻接点未访问完;已访问且其邻接点已访问完。
下面用0,1,2表示这三种状态。
前面已提到,若dfs(v)结束前出现顶点u到v的回边,则图中必有包含顶点v和u的回路。
对应程序中v的状态为1,而u是正访问的顶点,若我们找出u的下一邻接点的状态为1,就可以输出回路了。
voidPrint(intv,intstart)//输出从顶点start开始的回路。
{for(i=1;i<=n;i++)
if(g[v][i]!
=0&&visited[i]==1)//若存在边(v,i),且顶点i的状态为1。
{printf(“%d”,v);if(i==start)printf(“\n”);elsePrint(i,start);break;}//if
}//Print
voiddfs(intv)
{visited[v]=1;
for(j=1;j<=n;j++)
if(g[v][j]!
=0)//存在边(v,j)
if(visited[j]!
=1){if(!
visited[j])dfs(j);}//if
else{cycle=1;Print(j,j);}
visited[v]=2;
}//dfs
voidfind_cycle()//判断是否有回路,有则输出邻接矩阵。
visited数组为全局变量。
{for(i=1;i<=n;i++)visited[i]=0;
for(i=1;i<=n;i++)if(!
visited[i])dfs(i);
}//find_cycle
14.[题目分析]有几种方法判断有向图是否存在环路,这里使用拓扑排序法。
对有向图的顶点进行拓扑排序,若拓扑排序成功,则无环路;否则,存在环路。
题目已假定有向图用十字链表存储,为方便运算,在顶点结点中,再增加一个入度域indegree,存放顶点的入度。
入度为零的顶点先输出。
为节省空间,入度域还起栈的作用。
值得注意的是,在邻接表中,顶点的邻接点非常清楚,顶点的单链表中的邻接点域都是顶点的邻接点。
由于十字链表边(弧)结点个数与边(弧)个数相同(不象无向图边结点个数是边的二倍),因此,对某顶点v,要判断其邻接点是headvex还是tailvex。
intTopsor(OrthListg)
//判断以十字链表为存储结构的有向图g是否存在环路,如是,返回1,否则,返回0。
{inttop=0;//用作栈顶指针
for(i=1;i<=n;i++)//求各顶点的入度。
设有向图g有n个顶点,初始时入度域均为0
{p=g[i].firstin;//设顶点信息就是顶点编号,否则,要进行顶点定位
while(p)
{g[i].indegree++;//入度域增1
if(p->headvex==i)p=p->headlink;elsep=p->taillink;//找顶点i的邻接点
}//while(p)}//for
for(i=1;i<=n;i++)//建立入度为0的顶点的栈
if(g[i].indegree==0){g[i].indegree=top;top=i;}
m=0;//m为计数器,记输出顶点个数
while(top<>0)
{i=top;top=g[top].indegree;m++;//top指向下一入度为0的顶点
p=g[i].firstout;
while(p)//处理顶点i的各邻接点的入度
{if(p->tailvex==i)k=p->headvex;elsek=p->tailvex;}//找顶点i的邻接点
g[k].indegree--;//邻接点入度减1
if(g[k].indegree==0){g[k].indegree=top;top=k;}//入度为0的顶点再入栈
if(p->headvex==i)p=p->headlink;elsep=p->taillink;//找顶点i的下一邻接点
}//while(p)
}//while(top<>0)
if(m(1);//有向图存在环路
elsereturn(0);//有向图无环路
}//算法结束
15.intFirstAdj(AdjMuListg,vertypev)
//在邻接多重表g中,求v的第一邻接点,若存在,返回第一邻接点,否则,返回0。
{i=GraphLocateVertex(g,v);//确定顶点v在邻接多重表向量中的下标,不考虑不存在v的情况。
p=g[i].firstedge;//取第一个边结点。
if(p==null)return(0);
else{if(ivex==i)return(jvex);elsereturn(ivex);}
//返回第一邻接点,ivex和jvex中必有一个等于i
}//FirstAdj
16.[题目分析]本题应使用深度优先遍历,从主调函数进入dfs(v)时,开始记数,若退出dfs()前,已访问完有向图的全部顶点(设为n个),则有向图有根,v为根结点。
将n个顶点从1到n编号,各调用一次dfs()过程,就可以求出全部的根结点。
题中有向图的邻接表存储结构、记顶点个数的变量、以及访问标记数组等均设计为全局变量。
建立有向图g的邻接表存储结构参见上面第2题,这里只给出判断有向图是否有根的算法。
intnum=0,visited[]=0//num记访问顶点个数,访问数组visited初始化。
constn=用户定义的顶点数;
AdjListg;//用邻接表作存储结构的有向图g。
voiddfs(v)
{visited[v]=1;num++;//访问的顶点数+1
if(num==n){printf(“%d是有向图的根。
\n”,v);num=0;}//if
p=g[v].firstarc;
while(p)
{if(visied[p->adjvex]==0)dfs(p->adjvex);
p=p->next;}//while
visited[v]=0;num--;//恢复顶点v
}//dfs
voidJudgeRoot()
//判断有向图是否有根,有根则输出之。
{staticinti;
for(i=1;i<=n;i++)//从每个顶点出发,调用dfs()各一次。
{num=0;visited[1..n]=0;dfs(i);}
}//JudgeRoot
算法中打印根时,输出顶点在邻接表中的序号(下标),若要输出顶点信息,可使用g[i].vertex。
17.[题目分析]使用图的遍历可以求出图的连通分量。
进入dfs或bfs一次,就可以访问到图的一个连通分量的所有顶点。
voiddfs()
{visited[v]=1;printf(“%3d”,v);//输出连通分量的顶点。
p=g[v].firstarc;
while(p!
=null)
{if(visited[p->adjvex==0])dfs(p->adjvex);
p=p->next;
}//while
}//dfs
voidCount()
//求图中连通分量的个数
{intk=0;staticAdjListg;//设无向图g有n个结点
for(i=1;i<=n;i++)
if(visited[i]==0){printf("\n第%d个连通分量:
\n",++k);dfs(i);}//if
}//Count
算法中visited[]数组是全程变量,每个连通分量的顶点集按遍历顺序输出。
这里设顶点信息就是顶点编号,否则应取其g[i].vertex分量输出。
18.voidbfs(AdjListGL,vertypev)
//从v发广度优先遍历以邻接表为存储结构的无向图GL。
{visited[v]=1;
printf("%3d",v);//输出第一个遍历的顶点。
QueueInit(Q);QueueIn(Q,v);//先置空队列,然后第一个顶点v入队列,设队列容量足够大
while(!
QueueEmpty(Q))
{v=QueueOut(Q);p=GL[v].firstarc;//GL是全局变量,v入队列。
while(