您现在的位置:学赛首页 > 自考学院 > 数据结构与算法 > 正文
第7章图(算法设计)习题练习答案
http://www.educity.cn 作者:不详 来源: 2006年9月4日 发表评论 进入社区

7.16 试在无向图的邻接矩阵和邻接链表上实现如下算法:
 (1)往图中插入一个顶点
 (2)往图中插入一条边
 (3)删去图中某顶点
 (4)删去图中某条边

解:
  (一)用邻接矩阵表示
 #define MaxVertexNum l00 //最大顶点数,应由用户定义
 typedef char VertexType; //顶点类型应由用户定义
 typedef int EdgeType; //边上的权值类型应由用户定义
 typedef struct{
   VextexType vexs[MaxVertexNum] //顶点表
   EdeType edges[MaxVertexNum][MaxVertexNum];
   //邻接矩阵,可看作边表
   int n,e; //图中当前的顶点数和边数
  }MGragh;

 (1)往图中插入一个顶点 
 void AddVertexMGraph(MGraph *G,VertexType x)
  {//往无向图的邻接矩阵中插入顶点
    if (G->n>=MaxVertexNum)
      Error("顶点数太多");
    G->vexs[G->n]=x;//将新顶点输入顶点表
    G->n++;//顶点数加1
  }

 (2)往图中插入一条边
 void AddedgeMGraph(MGraph *G,VertexType x,VertexType y)
  {//往无向图的邻接矩阵中插入边(x,y) 
   int i,j,k;
   i=-1;j=-1;
   for(k=0;k<G->n;k++)//查找X,Y的编号
    { if (g->vexs[k]===x) i=k;
     if (g->vexs[k]==y) j=k;
    }
   if (i==-1||j==-1) Error("结点不存在");
   else {//插入边(x,y)
        g->vex[i][j]=1;
        g->vex[j][i]=1;
        G->e++;//边数加1
      }
  }

 (3)删去图中某顶点
 void DeleteVertexMGraph(MGraph *G,VertexType x)
  {//无向图的邻接矩阵中删除顶点x
    int i,k,j;
    i=-1;
    for(k=0;k<G->n;k++)//查找X的编号
      if (g->vexs[k]=x) i=k;
    if (i==-1) Error("结点不存在");
    else {//删除顶点以及边
        k=0;//求出与x结点相关联的边数k
        for (j=0;j<G->n;j++)
          if (g->vexs[i][j]==0) k++;
          G->e=G->e-k;//设置新的边数
        for (k=i+1;k<G->n;k++)//在邻接矩阵中删除第i行
          for(j=0;j<G->n;j++)
            g->vexs[k-1][j]=g->vexs[k][j];
        for (k=i+1;k<G->n;k++)//在邻接矩阵中删除第i列
          for(j=0;j<G->n;j++)
            g->vexs[j][k-1]=g->vexs[j][k];
        G->n--;//总结点数-1
       }
  } 

 (4)删去图中某条边
 void DeleteedgeMGraph(MGraph *G,VertexType x,VertexType y)
   {//无向图的邻接矩阵中删除边(x,y)
     int i,j,k;
     i=-1;j=-1;
     for(k=0;k<G->n;k++)//查找X,Y的编号
       { if (g->vexs[k]=x) i=k;
        if (g->vexs[k]=y) j=k;
       }
     if (i==-1||j==-1) Error("结点不存在");
     else if (g->vexs[i][j]!=1)
         {//删除边(x,y)
           g->vex[i][j]=0;
           g->vex[j][i]=0;
           G->e--;//边数加1
         }
   }

(二)用邻接表表示 

 typedef struct node{//边表结点
    int adjvex; //邻接点域
    struct node *next; //链域
     //若要表示边上的权,则应增加一个数据域
   }EdgeNode;
 typedef struct vnode{ //顶点表结点
     VertexType vertex; //顶点域
    EdgeNode *firstedge;//边表头指针
    }VertexNode;
 typedef VertexNode AdjList[MaxVertexNum];//AdjList是邻接表类型
 typedef struct{
    AdjList adjlist;//邻接表
    int n,e; 图中当前顶点数和边数 
   }ALGraph; //对于简单的应用,无须定义此类型,可直接使用AdjList类型。 

 (1)往图中插入一个顶点
 void AddVertexALGraPh(ALGrahp *G,VertexType x)
   {//往无向图的邻接表表示中插入一个顶点
     if (G->n>=MaxVertexNum)
        Error("顶点数太多");
     G->adjlist[G->n].vertex=x;//将新顶点输入顶点表
     G->adjlist[G->n].firstedge=NULL;//边表置为空表
     G->n++;//顶点数加1
   }

 (2)往图中插入一条边
 void AddedgeALGraPh(ALGrahp *G,VertexType x,VertexType y)
   {//往无向图的邻接表中插入边(x,y)
     int i,j,k;
     EdgeNode *s;
     i=-1;j=-1;
     for(k=0;k<G->n;k++)//查找X,Y的编号
       { if (G->adjlist[k].vertex==x) i=k;
        if (G->adjlist[k].vertex==y) j=k;
       }
     if (i==-1||j==-1) Error("结点不存在");
     else {
         s=G->adjlist[i].firstedge;
         while ((s)&&(s->adjvex!=j)//查看邻接表中有无(x,y)
           s=s->next;
         if (!s)//当邻接表中无边(x,y),插入边(x,y)
           { 
             s=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成边表结点
             s->adjvex=j; //邻接点序号为j
             s->next=G->adjlist[i].firstedge;
             G->adjlist[i].firstedge=s;//将新结点*s插入顶点x的边表头部
             s=(EdgeNode *)malloc(sizeof(EdgeNode));
             s->adjvex=i; //邻接点序号为i
             s->next=G->adjlist[j].firstedge;
             G->adjlist[j].firstedge=s; //将新结点*s插入顶点x的边表头部
             G->e++;//边数加1
           }
        }
   } 

 (3)删去图中某顶点
 void DeleteVertexALGraPh(ALGrahp *G,VertexType x)
  {//无向图的邻接表中删除顶点x 
   int i,k,j;
   EdgeNode *s,*p,*q;
   i=-1;
   for(k=0;k<G->n;k++)//查找X的编号
    if (G->adjlist[k].vertex==x) i=k;
   if (i==-1) Error("结点不存在");
   else {//删除与x相关联的边
       s=G->adjlist[i].firstedge;
       while (s)
         {p=G->adjlist[s->adjvex].firstedge;//删除与i相关联的其他结点边表中表结点
          if (p)&&(p->adjvex==i)//是第一个边表结点,修改头指针
           {G->adjlist[s->adjvex].firstedge=p->next;
            free(p);
           }
          else//不是第一个 边表结点,查找并删除
            {while (p)&&(p->next)&&(p->next->adjvex==i)
                 p=p->next;
             q=p->next;
             p->next=q->next;free(q);
            }
          q=s;s=s->next;free(q);//在i结点的边表中删除表结点
          G->e--;
         } 
       //调整顶点表
       for (j=i;j<G->n-1;j++)
         {G->adjlist[j].firstedge=G->adjlist[j+1].firstedge;
           G->adjlist[j].vertex=G->adjlist[j+1].vertex;
         }
       G->n--;
      }
  } 

 (4)删去图中某条边
 void DeleteedgeALGraPh(ALGraph *G,VertexType x,VertexType y)
  {//往无向图的邻接表中删除边(x,y)
   int i,j,k;
   EdgeNode *s,*p;
   i=-1;j=-1;
   for(k=0;k<G->n;k++)//查找X,Y的编号
    { if (G->adjlist[k].vertex==x) i=k;
     if (G->adjlist[k].vertex==y) j=k;
    }
   if (i==-1||j==-1) Error("结点不存在");
   else {
        s=G->adjlist[i].firstedge;//在i的边表中删除值为j的边表结点
        if (s)&&(s->adjvex==j) 
         {G->adjlist[i].firstedge=s->next;
          free(s);
         } 
        else{
           while (s)&&(s->next)&&(s->next->adjvex!=j)
              s=s->next;
           if (!s->next) Error("无此边");
           else 
             {p=s->next;s=p->next;free(p);
             }
          }
        s=G->adjlist[j].firstedge;//在j的边表中删除值为i的边表结点
        if (s)&&(s->adjvex==i) 
          {G->adjlist[j].firstedge=s->next;
           free(s);
          } 
        else{
           while (s)&&(s->next)&&(s->next->adjvex!=j)
             s=s->next;
           p=s->next;s=p->next;free(p);
          }      
        G->e--;
      }
   } 

7.17 下面的伪代码是一个广度优先搜索算法,试以图7.29(下图)中的v0为源点执行该算法,请回答下述问题:


 (1)对图中顶点vn+1,它需入队多少次?它被重复访问多少次?
 (2)若要避免重复访问同一个顶点的错误,应如何修改此算法?

 void BFS(ALGraph *G,int k)
  {//以下省略局部变量的说明,visited各分量初值为假
   InitQueue(&Q);//置空队列
   EnQueue(&Q,k);//k入队
   while(!QueueEmpty(&Q)){
     i=DeQueue(&Q);//vi出队
     visited[i]=TRUE;//置访问标记
     printf("%c",G->adjlist[i].vertex;//访问vi
     for(p=G->adjlist[i].firstedge;p;p=p->next)
       //依次搜索vi的邻接点vj(不妨设p->adjvex=j)
       if(!visited[p->adjvex])//若vj没有访问过
         EnQueue(&Q,p->adjvex);//vj入队
    }//endofwhile
   }//BFS


 (1)对图中顶点vn+1,它需入队n次,它被重复访问n次。

 (2)若要避免重复访问同一个顶点的错误,应作如下修改:
  void BFS(ALGraph *G,int k)
   {//以下省略局部变量的说明,visited各分量初值为假
     InitQueue(&Q);//置空队列
     EnQueue(&Q,k);//k入队
     visited[i]=TRUE;//置访问标记
     printf("%c",G->adjlist[i].vertex;//访问vi
     while(!QueueEmpty(&Q)){
        i=DeQueue(&Q);//vi出队
        for(p=G->adjlist[i].firstedge;p;p=p->next)
          //依次搜索并访问vi的未访问邻接点vj(不妨设p->adjvex=j)
          if(!visited[p->adjvex])//若vj没有访问过
            {printf("%c",G->adjlist[i].vertex;//访问vj
             EnQueue(&Q,p->adjvex);//vj入队
            }
         }//endofwhile
   }//BFS

7.18 试以邻接表和邻接矩阵为存储结构,分别写出基于DFS和BFS遍历的算法来判别顶点vi和vj(i<>j)之间是否有路径。

答:
 
(1)基于采用邻接矩阵的DFS
  int pathDFSM(MGraph *G,int i,int j)
   { //以邻接矩阵为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0
    int k;
    visited[i]=TRUE;
    for(k=0;k<G->n;k++) //依次搜索vi的邻接点
     if(G->edges[i][k]==1&&!visited[k])
      if (k==j) return 1;//有路径相通
      else return(pathDFSM(G,k,j);
     return 0;//无路径相通
   }//DFSM

 (2)基于采用邻接表的DFS 
  int PATHDFS(ALGraph *G,int i,int j){ 
   //以邻接表为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0
   EdgeNode *p;
   visited[i]=TRUE; //标记vi已访问
   p=G->adjlist[i].firstedge; //取vi边表的头指针
   while(p){//依次搜索vi的邻接点vk,这里k=p->adjvex
     if (!visited[p->adjvex])//若vk尚未被访问
       if (p->adjvex==j)
         return 1;
       else ruturn PATHDFS(g,p->adjvex,j);//则以Vk为出发点向纵深搜索
     p=p->next; //找vi的下一邻接点
    }
   return 0;
  }//PATHDFS

 (3)基于邻接矩阵的BFS算法
  int pathBFSM(MGraph *G,int i,int j)
    {//以邻接矩阵为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0
     int k;
     CirQueue Q;
      initQueue(&Q);
     visited[i]=TRUE;
     EnQueue(&Q,i);
     while(!QueueEmpty(&Q)){
       i=DeQueue(&Q); //vi出队
       for(k=0;k<G->n;k++)//依次搜索vi的邻接点vk
           if(G->edges[i][k]==1&&!visited[k]){//vk未访问
             if (k==j) return 1;
             visited[k]=TRUE;
             EnQueue(&Q,k);//访问过的vk人队
            }
      }//endwhile
     return 0;
  }//pathBFSM

 (4)基于邻接表为存储结构的BFS算法 
  int BFS(ALGraph *G,int i,int j)
    {//以邻接表为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0
     int i;
     CirQueue Q; //须将队列定义中DataType改为int
     EdgeNode *p;
     InitQueue(&Q);//队列初始化
     visited[i]=TRUE; 
     EnQueue(&Q,i);//vi已访问,将其人队。(实际上是将其序号人队)
     while(!QueueEmpty(&Q)){//队非空则执行
       i=DeQueue(&Q); //相当于vi出队
       p=G->adjlist[i].firstedge; //取vi的边表头指针
         while(p){//依次搜索vi的邻接点vk(令p->adjvex=k)
            if(!visited[p->adjvex]) //若vk未访问过
              if (p->adjvex==j)
                  return 1;
              else{
                  visited[P->adjvex]=TRUE; 
                  EnQueue(&Q,p->adjvex);
                }//访问过的vk人队
                        p=p->next;//找vi的下一邻接点
                      }//endwhile
            }//endwhile
          return 0;
    }//end of pathBFS

7.19 试分别写出求DFS和BFS生成树(或生成森林)的算法,要求打印出所有的树边。
答:
   (1)求DFS生成树
 typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为1
 Boolean visited[MaxVertexNum]; //访问标志向量是全局量
 void DFSTraverseTREE(ALGraph *G)
  { //求深度优先生成树(以邻接表表示的图G),而以邻接矩阵表示G时,
   //算法完全与此相同,只要将DFSTree(G,i)改为DFSMTree(G,i)即可
   int i;
   for(i=0;i<G->n;i++)
     visited[i]=FALSE; //标志向量初始化
   for(i=0;i<G->n;i++)
     if(!visited[i]) //vi未访问过
       DFSTree(G,i); //以vi为源点开始DFS搜索,求DFS生成树的边
  }//DFSTraverse

 void DFSTree(ALGraph *G,int i){ 
  //以vi为出发点对邻接表表示的图G进行深度优先搜索,打印生成树(生成森林)的边
  EdgeNode *p;
  visited[i]=TRUE; //标记vi已访问
  p=G->adjlist[i].firstedge; //取vi边表的头指针
  while(p){//依次搜索vi的邻接点vj,这里j=p->adjvex
       if (!visited[p->adjvex])//若vj尚未被访问
         {printf("(%c,%c)\n",G->adjlist[i].vertex,G->adjlist[p->adjvex].vertex) ;
          // 打印边
          DFSTree(g,p->adjvex);//则以Vj为出发点向纵深搜索
         }
       p=p->next; //找vi的下一邻接点
      }
 }//DFS

 void DFSMTree(MGraph *G,int i)
  { //以vi为出发点对邻接矩阵表示的图G进行深度优先搜索,打印生成树(生成森林)的边
   int j;
   visited[i]=TRUE;
   for(j=0;j<G->n;j++) //依次搜索vi的邻接点
     if(G->edges[i][j]==1&&!visited[j])
      {
       printf("(%c,%c)\n",G->vexs[i],G->vexs[j]);// 打印边
       DFSMTree(G,j);//(vi,vj)∈E,且vj未访问过,故vj为新出发点
      }
  }//DFSMTree


  (2)求BFS生成树
 typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为1
 Boolean visited[MaxVertexNum]; //访问标志向量是全局量
 void BFSTraverseTREE(ALGraph *G)
  { //求广度优先生成树(以邻接表表示的图G),而以邻接矩阵表示G时,
   //算法完全与此相同,只要将BFSTree(G,i)改为BFSMTree(G,i)即可
      int i;
      for(i=0;i<G->n;i++)
        visited[i]=FALSE; //标志向量初始化
      for(i=0;i<G->n;i++)
    if(!visited[i]) //vi未访问过
     BFSTree(G,i); //以vi为源点开始BFS搜索,求BFS生成树的边
  }//BFSTraverse

 void BFSTree(ALGraph*G,int k)
   {// 以vk为源点对用邻接表表示的图G进行广度优先搜索,并求出BFS生成树边
     int i;
     CirQueue Q; //须将队列定义中DataType改为int
     EdgeNode *p;
     InitQueue(&Q);//队列初始化
     visited[k]=TRUE; 
     EnQueue(&Q,k);//vk已访问,将其人队。(实际上是将其序号人队)
     while(!QueueEmpty(&Q)){//队非空则执行
       i=DeQueue(&Q); //相当于vi出队
       p=G->adjlist[i].firstedge; //取vi的边表头指针
       while(p){//依次搜索vi的邻接点vj(令p->adjvex=j)
         if(!visited[p->adivex]){ //若vj未访问过
           printf("(%c,%c)\n",G->adjlist[i].vertex,G->adjlist[p->adjvex].vertex);
            //打印边
           visited[P->adjvex]=TRUE; 
           EnQueue(&Q,p->adjvex);//访问过的vj人队
          }//endif
         p=p->next;//找vi的下一邻接点
        }//endwhile
      }//endwhile
   }//end of BFSTree 

 void BFSMTree(MGraph *G,int k)
  {// 以vk为源点对用邻接矩阵表示的图G进行广度优先搜索,并求出BFS生成树边
    int i,j; 
    CirQueue Q;
        InitQueue(&Q);
    visited[k]=TRUE;
    EnQueue(&Q,k);
    while(!QueueEmpty(&Q)){
      i=DeQueue(&Q); //vi出队
      for(j=0;j<G->n;j++)//依次搜索vi的邻接点vj
        if(G->edges[i][j]==1&&!visited[j]){//vi未访问
          printf("(%c,%c)\n",G->vexs[i],G->vexs[j]);//打印边
          visited[j]=TRUE;
          EnQueue(&Q,j);//访问过的vj人队
         }
     }//endwhile
  }//BFSMTree

7.20 写一算法求连通分量的个数并输出各连通分量的顶点集。
答:
 typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为1
 Boolean visited[MaxVertexNum]; //访问标志向量是全局量
 void DFSTraverse(ALGraph *G)
  { //深度优先遍历以邻接表表示的图G,求连通分量的个数和各连通分量的顶点集
   int i;
   for(i=0;i<G->n;i++)
    visited[i]=FALSE; //标志向量初始化
   j=0;//连通分量个数计数器
   for(i=0;i<G->n;i++)
    if(!visited[i]) //vi未访问过
     {j++;
      printf("connected component %d:{",j);
      DFS(G,i); //以vi为源点开始DFS搜索
      printf("}\n");
     }
  }//DFSTraverse

 void DFS(ALGraph *G,int i){ 
  //以vi为出发点对邻接表表示的图G进行深度优先搜索
  EdgeNode *p;
  printf("%c,",G->adjlist[i].vertex);//访问顶点vi
  visited[i]=TRUE; //标记vi已访问
  p=G->adjlist[i].firstedge; //取vi边表的头指针
  while(p){//依次搜索vi的邻接点vj,这里j=p->adjvex
    if (!visited[p->adjvex])//若vi尚未被访问
     DFS(g,p->adjvex);//则以Vj为出发点向纵深搜索
    p=p->next; //找vi的下一邻接点
   }
 }//DFS

7.21 设图中各边的权值都相等,试以邻接矩阵和邻接表为存储结构,分别写出算法:
 (1)求顶点vi到顶点vj(i<>j)的最短路径
 (2)求源点vi到其余各顶点的最短路径
  要求输出路径上的所有顶点(提示:利用BFS遍历的思想)

答:

  (1)求顶点vi到顶点vj(i<>j)的最短路径
 int shortestpath(ALGraph*G,int i,int j)
  {// 对邻接表表示的图G,求顶点vi到顶点vj(i<>j)的最短路径
   int dist[MaxVertexNum];
   CirQueue Q; //须将队列定义中DataType改为int
   EdgeNode *p;
   int k;
   for(k=0;k<G->n;k++)
    dist[k]=0; //距离向量初始化
   InitQueue(&Q);//队列初始化
   visited[i]=TRUE; 
   EnQueue(&Q,i);
   while(!QueueEmpty(&Q)){//队非空则执行
     i=DeQueue(&Q); //相当于vi出队
     p=G->adjlist[i].firstedge; //取vi的边表头指针
     while(p){//依次搜索vi的邻接点vk(令p->adjvex=k)
       if(!visited[p->adjvex]){ //若vj未访问过
          dist[p->adjvex]++;
          if (p->adjvex==j) return dist[p->adjvex];
             visited[P->adjvex]=TRUE; 
          EnQueue(&Q,p->adjvex);//访问过的vk人队
        }//endif
       p=p->next;//找vi的下一邻接点
      }//endwhile
    }//endwhile
  }//end of shortestpath

 int BFSM(MGraph *G,int i,int j)
  {// 对邻接链表表示的图G,求顶点vi到顶点vj(i<>j)的最短路径
   int dist[MaxVertexNum],k;
   CirQueue Q;
      initQueue(&Q);
   for(k=0;k<G->n;k++)
    dist[i]=0; //距离向量初始化
   visited[k]=TRUE;
   EnQueue(&Q,i);
   while(!QueueEmpty(&Q)){
     i=DeQueue(&Q); //vi出队
     for(k=0;k<G->n;k++)//依次搜索vi的邻接点vk
       if(G->edges[i][k]==1&&!visited[k]){//vk未访问
         dist[k]++;
         if (k==j) return dist[j];
         visited[k]=TRUE;
         EnQueue(&Q,k);//访问过的vk人队
        }
    }//endwhile
  }//BFSM


(2)求源点vi到其余各顶点的最短路径

 void shortestpath(ALGraph*G,int i)
  {// 对邻接表表示的图G,求顶点vi到顶点vj(i<>j)的最短路径
   int dist[MaxVertexNum];
   int pre[MaxVertexNum];//pre[k]中存放vi到vk路径中,vk的前趋的序号
   CirQueue Q; //须将队列定义中DataType改为int
   EdgeNode *p;
   int k,j;
   for(k=0;k<G->n;k++)
     {dist[k]=0; //距离向量初始化
      pre[k]=k;
     }
   InitQueue(&Q);//队列初始化
   visited[i]=TRUE; 
   EnQueue(&Q,i);
   while(!QueueEmpty(&Q)){//队非空则执行
     i=DeQueue(&Q); //相当于vi出队
     p=G->adjlist[i].firstedge; //取vi的边表头指针
     while(p){//依次搜索vi的邻接点vk(令p->adjvex=k)
       if(!visited[p->adjvex]){ //若vj未访问过
         dist[p->adjvex]++;
         pre[p->adjvex]=i;
         visited[P->adjvex]=TRUE; 
         EnQueue(&Q,p->adjvex);//访问过的vk人队
        }//endif
       p=p->next;//找vi的下一邻接点
      }//endwhile
    }//endwhile
  for(k=0;k<G->n;k++)// 打印各顶点的最短路径和长度
    {printf("path of %c is %d:",G->adjlist[k].vertex,dist[k]);
       j=k;
       printf("%c",G->adjlist[k].vertex);
       do
      {
       j=pre[j];
       print("<-%c",G->adjlist[j].vertex);
      }
     while (j!=i);
     printf("\n");
    }
  }//end of shortestpath

 void shortestpathBFSM(MGraph *G,int i)
  {// 对邻接矩阵表示的图G,求顶点vi到其他顶点的最短路径
   int dist[MaxVertexNum],k,j;
   int pre[MaxVertexNum];//pre[k]中存放vi到vk路径中,vk的前趋的序号

   CirQueue Q;
   initQueue(&Q);
   for(k=0;k<G->n;k++)
        {dist[k]=0; //距离向量初始化
     pre[k]=k;
    }
   visited[k]=TRUE;
   EnQueue(&Q,i);
   while(!QueueEmpty(&Q)){
     i=DeQueue(&Q); //vi出队
     for(k=0;k<G->n;k++)//依次搜索vi的邻接点vk
      if(G->edges[i][k]==1&&!visited[k]){//vk未访问
        dist[k]++;
        pre[k]=i;
        visited[k]=TRUE;
        EnQueue(&Q,k);//访问过的vk人队
       }
    }//endwhile
   for(k=0;k<G->n;k++)// 打印各顶点的最短路径和长度
     {printf("path of %c is %d:",G->vertex[k],dist[k]);
      j=k;
      printf("%c",G->vertex[k]);
      do
       {
        j=pre[j];
        printf("<-%c",G->vertex[j]);
           }
      while (j!=i);
      printf("\n");
     }
  }//shortestpathBFSM

7.22 以邻接表为存储结构,写一个基于DFS遍历策略的算法,求图中通过某顶点vk的简单回路(若存在)。
答:
 int circleDFS(ALGraph *G,int k){ 
  //以vk为出发点对邻接表表示的图G,求简单回路,若存在返回1,否则返回0
  EdgeNode *p;
  printf("%c",G->adjlist[k].vertex);//访问顶点vk
  visited[k]=TRUE; //标记vk已访问
  p=G->adjlist[k].firstedge; //取vk边表的头指针
  while(p){//依次搜索vk的邻接点vj,这里j=p->adjvex
    if (!visited[p->adjvex])//若vj尚未被访问
      DFS(G,p->adjvex);//则以Vj为出发点向纵深搜索
    else if (p->adjvex==k) 
        {printf("%c",G->adjlist[k].vertex);return 1;}
    p=p->next; //找vk的下一邻接点
   }
  return 0;
 }//DFS

7.23 写一算法求有向图的所有根(若存在),分析算法的时间复杂度。
答:

 typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为1
 Boolean visited[MaxVertexNum]; //访问标志向量是全局量
 void DFSTraverse(ALGraph *G)
  { //对以邻接表表示的有向图G,求所有根(以邻接矩阵表示G时,算法完全与此相同)
   int i,j;
   for (j=0;j<G->n;j++)
    {
     for(i=0;i<G->n;i++)
      visited[i]=FALSE; //标志向量初始化
     DFS(G,j); //以vj为源点开始DFS搜索,也可用BFS(G,j)
     i=0;
     while(i<G->n)&&(visited[i]==TRUE)
       i++;
     if (i==G->n) printf("root:%c",G->adjlist[j].vertex);
    }
  }//DFSTraverse

  该算法的为二重循环,若调用的DFS算法的复杂度为O(n+e),所以该算法的时间复杂度为O(n(n+e))
  若调用的DFSM算法的复杂度为O(n*n),所以该算法的时间复杂度为O(n^3)

7.24 改写7.5节的算法Print,使输出的从源点到各终点的最短路径是正向的。(提示:使用栈暂存路径)

答:
 void print(path p,distance d)
  {//输出最短路径及其长度,从源点到各终点的最短路径是正向的
   int i,pre;
   SeqStack S; //定义一个栈
   InitStack (&s);
   for(i=0;i<n;i++){//依次打印各顶点i的最短路径及其长度
     printf("\n distance:%d,path:",d[i]);//输出终点i的最短距离
     Push(&S, i);//终点i入栈
     pre=p[i];//找终点i的前趋
     while (pre!=-1)
      {push(&s,pre);
       pre=p[pre];//继续找前趋
      }//endwhile
     printf("%d",pop(&s));
     while (!StackEmpty(S))
       printf(",%d",pop(&s));
   }//endfor 
  }//endprint

7.25 对7.6节的NonSuccFirstTopSort算法求精,分别以邻接矩阵和邻接表作为存储结构,写出其具体算法,并分析算法的时间。
答:


  (1)以邻接矩阵作为存储结构
 void NonSuccFirstTopSort(MGraph G){//优先输出无后继的顶点
   int outdegree[MaxVertexNum];//出度向量,此处MaxVertexNum>=G.n
   SeqStack S,T;//将栈中data向量的基类型改为int
   int i,j,count=0;//count对输出的顶点数目计数,初值为0
   for(i=0;i<G.n;i++)
    outdegree[i]=0;
   for(i=0;i<G.n;i++)
     for(j=0;j<G.n;j++)
       outdegree[i]=outdegree[i]+g.edge[i][j];
   InitStack(&s);
   for(i=0;i<G.n;i++)
    if (outdegree[i]==0) 
      Push(&S, i);//出度为0的顶点i入栈
   InitStack(&T);
   while(!StackEmpty(S))//栈非空,有出度为0的顶点
     {i=pop(&s);
      Push(&T, i);
      count++;//顶点计数加1
      for (j=0;j<G.n;j++)//修改以i为弧头的弧的弧尾顶点的出度
        if (G.edge[j][i]==1)
         {G.edge[j][i]=0;
          outdegree[j]--;
          if (outdegree[j]==0)//将新生成的出度为0的顶点入栈 
             Push(&S, j);//出度为0的顶点j入栈
         }//end of if
     }//end of while
   if (count<G.n)
     printf("G中存在有向环,排序失败!");
   else{//输出拓扑序列
      while (!StackEmpty(T))
        printf("%c",G.vertex[pop(&T)]);
     }
  }

  (2)以邻接表作为存储结构
 void NonSuccFirstTopSort(ALGraph G){
   //优先输出无后继的顶点,此处用逆邻接表存储
   int outdegree[MaxVertexNum];//出度向量,此处MaxVertexNum>=G.n
   SeqStack S,T;//将栈中data向量的基类型改为int
   int i,j,count=0;//count对输出的顶点数目计数,初值为0
   EdgeNode *p;
   for(i=0;i<G.n;i++)
    outdegree[i]=0;
   for(i=0;i<G.n;i++)
    for(p=G.adjlist[i].firstedge;p;p=p->next)//扫描i的入边表
      outdegree[p->adjvex]++;//设p->adjvex=j,则将<j,i>的起点j出度加1
   InitStack(&s);
   for(i=0;i<G.n;i++)
   if (outdegree[i]==0) 
    Push(&S, i);//出度为0的顶点i入栈
   InitStack(&T);
   while(!StackEmpty(S))//栈非空,有出度为0的顶点
    {i=pop(&s);
     Push(&T, i);
     count++;//顶点计数加1
     for(p=G.adjlist[i].firstedge;p;p=p->next)
      //修改以i为弧头的弧的弧尾顶点的出度
      {
       j=p->adjvex;
       outdegree[j]--;
       if (outdegree[j]==0)//将新生成的出度为0的顶点入栈 
          Push(&S, j);//出度为0的顶点j入栈
      }//end of for 
    }//end of while
   if (count<G.n)//输出顶点数小于n
     printf("G中存在有向环,排序失败!");
   else{//输出拓扑序列
      while (!StackEmpty(T))
        printf("%c",G.adjlist[pop(&T)].vertex);
     }
  }

7.26 设一个有向图DAG,试以邻接矩阵和邻接表作为存储结构,写出对7.6节的DFSTopSort求精算法。为什么有向图不是DAG时,该算法不能正常工作?
答:

  (1)以邻接矩阵作为存储结构
 void DFSTopSort(MGraph G,int i,SeqStack T){
   EdgeNode *p;
   visited[i]=TRUE;
   for(j=0;j<G.n;j++)
    if (G.edge[i][j]==1)&&(!visited[j])
      FFSTopSort(G,j,T);
    push(&T,i);
  }

  (2)以邻接表作为存储结构
 void DFSTopSort(ALGraph G,int i,SeqStack T){
   int j;
   visited[i]=TRUE;
   for(p=G.adjlist[i].firstedge;p;p=p->next)
     if (!visited[p->adjvex])//若vj尚未被访问
       DFSTopSort(G,p->adjvex,T);
     push(&T,i);
  }

  当有向图不是DAG图时,该算法不能检测出回路,仍然能给出包含所有顶点的序列,所以该算法不能正常工作。

7.27 利用拓扑排序算法的思想写一算法判别有向图中是否存在有向环,当有向环存在时,输出构成环的顶点。
答:
 typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为1
 Boolean visited[MaxVertexNum]; //访问标志向量是全局量
 int i;
 for(i=0;i<G->n;i++)
  visited[i]=FALSE; //标志向量初始化

  以邻接表作为存储结构
 void NonSuccFirstTopSort(ALGraph G){
   //优先输出无后继的顶点,此处用逆邻接表存储
   int outdegree[MaxVertexNum];//出度向量,此处MaxVertexNum>=G.n
   SeqStack S;//将栈中data向量的基类型改为int
   int i,j,count=0;//count对输出的顶点数目计数,初值为0
   EdgeNode *p;
   for(i=0;i<G.n;i++)
    { outdegree[i]=0; 
     visited[i]=FALSE; //标志向量初始化
    }
   for(i=0;i<G.n;i++)
     for(p=G.adjlist[i].firstedge;p;p=p->next)//扫描i的入边表
       outdegree[p->adjvex]++;//设p->adjvex=j,则将<j,i>的起点j出度加1
   InitStack(&s);
   for(i=0;i<G.n;i++)
    if (outdegree[i]==0) 
     Push(&S, i);//出度为0的顶点i入栈
   while(!StackEmpty(S))//栈非空,有出度为0的顶点
    {i=pop(&s);visited[i]=TRUE;
     count++;//顶点计数加1
     for(p=G.adjlist[i].firstedge;p;p=p->next)
      //修改以i为弧头的弧的弧尾顶点的出度
      {
       j=p->adjvex;
       outdegree[j]--;
       if (outdegree[j]==0)//将新生成的出度为0的顶点入栈 
         Push(&S, j);//出度为0的顶点j入栈
      }//end of for 
    }//end of while
   if (count<G.n)//输出顶点数小于n
    {printf("G中存在有向环,排序失败!");
     for(i=0;i<G.n;i++)
       if (visited[i]==FALSE)
        printf("%c",G.adjlist[i].vertex);
    }
   else printf("G中无有向环!");
  }