学习 严蔚敏讲数据结构笔记15

来源:互联网 发布:做淘宝仓库打包员累吗 编辑:程序博客网 时间:2024/05/19 13:58

广度优先搜索遍历图

从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。

26_002

void BFSTraverse(Graph G, Status(*  Visit)(int v))

{

         for(v  = 0; v < G.vexnum; ++ v)

         {

                   visited[v]  = FALSE;

         }

         InitQueue(Q);  //置空的辅助队列Q

         for(v  = 0; v < G.vexnum; ++ v)

         {

                   if(!  visited[v])

                   {

                            //v尚未访问

                            EnQueue(Q,  v); //v入队列

                            visited[u]  = TRUE;

                            Visti(u);  //访问u

                            while(!  QueueEmpty(Q))

                            {

                                     DeQueue(Q,  u); //对头元素的出队并置u

                                     //访问u

                                     for(w  = FirstAdjVex(G, u); w != 0; w = NextAdjVex(G, u, w))

                                     {

                                               if(!  visited[w])

                                               {

                                                        EnQueue(Q,  w);

                                                        visited[w]  = TRUE;

                                                        Visit(w);

                                               }

                                               //u的尚未访问的邻接顶点w入队列Q

                                     }

                            };

                   }

         }

}//BFSTraverse

 

遍历的简单应用

1.      求一条从顶点i到顶点s的简单路径

27_001

void DFSearch(int v, int s, char *PATH)

{

         //从第v个顶点出发递归地深度优先遍历图G

         //求得一条从vs的简单路径,并记录在PATH

         visited[v]  = TRUE; //访问第v个顶点

         Append(PATH,  getVertex(v));

         for(w  = FirstAdjVex(v); w != 0 && !found; w = NextAdjVex(v))

         {

                   if(w  = s)

                   {

                            found  = TRUE;

                            Append(PATH,  W);

                   }

                   else

                   {

                            if(!  visited[w])

                            {

                                               DFSearch(w,  LP);

                            }

                   }

                   if(!  found)

                   {

                            Delete(PATH);

                   }

         }

}

2.      求两个顶点之间的一条路径长度最短的路径

基于广度优先搜索遍历,并修改链队列的结点结构及其入队列和出队列的算法。

1)  将链队列的结点改为“双链”结点,即结点中包含nextpriou两个指针;

2)  修改入队列的操作,插入新的的队尾结点时,令其priou域的指针指向刚刚出队列的结点,即当前的对头指针所指结点;

3)  修改出队列的操作,出队列时,仅移动对头指针,而不将对头结点从链表中删除。

27_003

typedef DuLinkList QueuePtr;

void InitQueue(LinkQueue &Q)

{

         Q.front  = Q.rear = (QueuePtr) malloc (sizeof(QNode));

         Q.front->next  = Q.rear->next = NULL;

}

 

void EnQueue(LinkQueue &Q, QelemType  e)

{

         p  = (QueuePtr) malloc(sizeof(QNode));

         p->data  = e;

         p->next  = NULL;

         p->priou  = Q.front;

         Q.rear->next  = p;

         Q.rear  = p;

}

 

void DeQueue(LinkQueue &Q, QelemType  &e)

{

         Q.front  = Q.front->next;

         e  = Q.front->data;

}

 

7.4 最小生成树

问题:假设要在n个城市之间建立通讯网络,则连同n个城市只需要修n-1条线路,如何在最节省经费的前提下建立这个通讯网?

该问题等价于:构造网的一棵最小生成树,即:在e条带权的边中选取n-1条(不构成回路),使“权值之和“为最小。

算法一:(普里姆算法)

可取图中任意一个顶点v作为生成树的根,之后若要网生成树上添加顶点w,则在顶点v和顶点w之间必定存在一条边,并且该边的权值在所有连同顶点vw之间的边中取最小值。

一般情况下,假设n个顶点分成两个集合:U(包含已落在生成树上的结点)和V-U(尚未落在生成树上的顶点),则在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。

记录从顶点集UV-U的代价最小的边的辅助数组:

27_004

struct

{

         VertexType  adjvex;

         VRTyped  lowcost;

}closedge[MAX_VERTEX_NUM];

k = LocateVex(G, u);

for(j = 0; j < G.vexnum; ++ j)

{

         //辅助数组初始化

         if(j  != k)

         {

                   closedge[j]  = (u, G.arcs[k][j].adj);

         }

         closeedge[k].lowcoset  = 0; //初始,U-(u)

         for(j  = 0; i < G.vexnum; ++ i)

         {

                   //在其余顶点中选择

                   k  = minimum(closedge); //求出T的一个小节点(k)

                   printf(colsedge[k].adjvex,  G.vexs[k]);

                   closedge[k].lowcost  = 0; //k顶点并入U

                   for(j  = 0; j < G.vexnum; ++ j)

                   {

                            if(G.arcs[k][j].adj  < closedge[j].lowcost)

                            {

                                     closedge[j]  = (G.vexs[k], G.arcs[k][j].adj);

                            }

                   }

         }

}

算法二:(克鲁斯卡尔算法)

为使生成树上边的权值之和最小,显然,其中每一台边的权值应该尽可能地小。克鲁斯卡尔算法的做法吉局势:先构造一个只含有n个顶点的子图SG,然后从权值最小的边喀什,若它的添加不使SG中产生回路,则在SG上加上这条边,如此重复,直至加上n-1条边为止。

算法:

构造非连通图ST=(V,{});k=i=0; while(k<n-1){++I;从边集E中选取第i条权值最小的边(u,v);若(u,v)加入ST后不会使ST中产生回路,则输出边(u,v);k++;}

由于普里姆算法的时间复杂度为O(n2),则适用于稠密图;而克鲁斯卡尔算法则需对e条边按权值进行排序,其时间复杂度为O(eloge),则适用域稀疏图。

 

7.5重(双)连通图和关节点

问题:若从一个连通图中删去任何一个顶点及其相关联的边,它仍为一个连通图的话,则该连通图被称为重(双)连通图。

若连通图中的某个顶点和其相关联的边被删去之后,该连通图被分割成两个或两个以上的连同分量,则称此顶点为关节点。

没有关节点的连通图为双连通图。

 

关节点的特征:

假设从某个顶点V0出发对连通图进行深度优先搜索遍历,则可得到一棵深度优先生成树,树上包含图的所有顶点。

若生成树的根结点,有两个或两个以上的分支,则此顶点(生成树的根)必为关节点;

对生成树上的任意一个“顶点”,若其某棵子树的跟或子树中的其它“顶点”没有和其祖先相通的回边,则该“顶点”必为关节点。

1)      V0为深度优先遍历的出发点

28_001

p = G.vertices[0].firstarc;

v = p->adjvex;

DFSArticul(G, v); //从第v顶点出发深度优先搜素

if(count < G.vexnum)

{

         //生成树的根至少有两棵子树

         printf(0,  G.vertices[0].data);//根是关节点

}

2)      对生成树上的顶点定义一个函数

29_002

low(v)=Min{visited[v], low[w], visted[k]}

对顶点v,若(在生成树上)存在一个子树根w,且low[w]>=visited[v]则顶点v为关节点。

对深度优先遍历算法作如下修改:

1.      visited[v]的值改为遍历过程中顶点的访问次序count

2.      遍历过程中求得low[v]=Min{visited[v],low[w],visited[k]}

3.      从子树遍历返回时,判别low[w]>=visited[v]?

29_003

min = visited[v0] = ++count; //设定low[v0]的初始值count计顶点访问次序

for(p = G.vertices[v0].firstarc; p;  p=p->nextarc)

{

         w  = p->adjvex; //wv0的邻接顶点

         if(visited[w]  == 0)

         {

                   //w未曾访问

                   DFSArticul(G,  w); //返回前求得low[w]

                   if(low[w]  < min)

                   {

                            min  = low[w];

                            if(low[w]  >= visited[v0])

                            {

                                     printf(v0,  G.vertices[v0].data); //输出关节点

                            }

                   }

         }

         else

         {

                   //w是回边上的顶点

                   if(visited[w]  < min)

                   {

                            min  = visited[w];

                   }

         }

         low[v0]  = min;

}

原创粉丝点击