1 / 28

7.4 图的连通性问题

7.4 图的连通性问题. 1. 无向图的连通分量. 图遍历时,对于连通图,无论是广度优先搜索还是深度优先搜索,仅需要调用一次搜索过程,即从任一个顶点出发,便可以遍历图中的各个顶点。对于非连通图,则需要多次调用搜索过程,而每次调用得到的顶点访问序列恰为各连通分量中的顶点集。. j=0; for(v=0; v < G.vernum; ++v) if(!visited[v]){ DFS(G, v); j++; }. A L M J B F C;  D E; G K H I;.

dougal
Download Presentation

7.4 图的连通性问题

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 7.4 图的连通性问题 1. 无向图的连通分量 图遍历时,对于连通图,无论是广度优先搜索还是深度优先搜索,仅需要调用一次搜索过程,即从任一个顶点出发,便可以遍历图中的各个顶点。对于非连通图,则需要多次调用搜索过程,而每次调用得到的顶点访问序列恰为各连通分量中的顶点集。 j=0; for(v=0; v < G.vernum; ++v) if(!visited[v]){ DFS(G, v); j++; }

  2. A L M J B F C;  D E; G K H I;

  3. 2. 无向图的生成树 一个连通图的生成树是一个极小连通子图,它含有图中全部顶点,但只有构成一棵树的(n-1)条边。 e<n-1 → 非连通图 e>n-1 → 有回路 e=n-1 → 不一定都是图的生成树 设G=(V,E)为连通图,则从图中任一顶点出发遍历图时,必定将E(G)分成两个集合T和B,其中T是遍历图过程中走过的边的集合,B是剩余的边的集合:T∩B=Φ,T∪B=E(G)。显然,G'=(V,T)是G的极小连通子图,即G'是G的一棵生成树。

  4. 由深度优先遍历得到的生成树称为深度优先生成树;由广度优先遍历得到的生成树称为广度优先生成树。这样的生成树是由遍历时访问过的n个顶点和遍历时经历的n-1条边组成。由深度优先遍历得到的生成树称为深度优先生成树;由广度优先遍历得到的生成树称为广度优先生成树。这样的生成树是由遍历时访问过的n个顶点和遍历时经历的n-1条边组成。 对于非连通图,每个连通分量中的顶点集和遍历时走过的边一起构成一棵生成树,各个连通分量的生成树组成非连通图的生成森林。 问题:以孩子兄弟链表作为森林(或树)的存储结构,如何建立无向图的深度优先生成森林或广度优先生成森林?

  5. 深度优先生成森林 广度优先生成森林

  6. void DFSForest(ALGraph G, CSTree &T) { T = NULL; for(v=0; v<G.vexnum; ++v) visited[v] = 0; for(v=0; v<G.vexnum; ++v) if( !visited[v]){ p = (CSTree) malloc(sizeof(CSNode)); *p = { G.vertices[v].data, NULL, NULL}; if( !T) T = p; else q->nextsibling = p; q = p; DFSTree(G, v, p); } }

  7. void DFSTree(Graph G, int v, CSTree &T) { ArcNode *a; CSTree p; visited[v] = 1; first = TRUE; for(a=G.vertices[v].firstarc; a; a=a->nextarc){ w = a->adjvex; if( !visited[w]){ p = (CSTree) malloc(sizeof(CSNode)); *p = {G.vertices[w].data, NULL, NULL}; if( first) { T->lchild = p; first = FALSE; } else q->nextsibling = p; q = p; DFSTree(G, w, q); }//if }//for }

  8. 3. 最小生成树 在一个连通网的所有生成树中, 各边的代价之和最小的那棵生成树称为该连通网的最小代价生成树(Minimum Cost Spanning Tree),简称为最小生成树(MST)。 最小生成树有如下重要性质(MST性质):  设 N=(V, {E}) 是一连通网,U 是顶点集V的一个非空子集。 若(u , v)是一条具有最小权值的边, 其中u∈U, v∈V-U, 则存在一棵包含边(u , v)的最小生成树。

  9. 用反证法来证明这个MST性质:  假设不存在这样一棵包含边(u , v)的最小生成树。 任取一棵最小生成树T,将(u , v)加入T中。根据树的性质,此时T中必形成一个包含(u , v)的回路, 且回路中必有一条边(u′, v′)的权值大于或等于(u , v)的权值。 删除(u′, v′),则得到一棵代价小于等于T的生成树T′,且T′为一棵包含边(u , v)的最小生成树。 这与假设矛盾, 故该性质得证。  利用MST性质来生成一个连通网的最小生成树。普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法便是利用了这个性质。

  10. 普里姆算法 (Prim算法,又称边割法) 1957年由Prim提出 假设N=(V, {E})是连通网,TE为最小生成树中边的集合。 Step1: 初始U={u0}(u0∈V), TE=φ;  Step2: 在所有u∈U, v∈V-U的边中选一条代价最小的边(u0, v0) 并入集合TE,同时将v0并入U;  Step3: 重复Step2,直到U=V为止。  此时,TE中必含有n-1条边,则T=(V, {TE})为N的最小生成树。  从一个平凡图开始,普利姆算法逐步增加U中的顶点, 可称为“加点法”。

  11. 普里姆算法求解最小生成树的过程

  12. struct { VertexType adjvex; //顶点名称 int lowcost;  } closedge[MAX_VERTEX_NUM]; 为了实现这个算法需要设置一个辅助数组closedge[],以记录从U到V-U具有最小代价的边。对每个顶点vi∈V-U,在辅助数组中存在一个分量closedge[vi],它包括两个域adjvex和lowcost,其中lowcost存储该边上的权, 显然有 closedge[i-1].lowcost = Min({cost(u, vi) | u∈U})

  13. void MiniSpanTree_PRIM( MGraph G, VertexType u) { k = LocateVex(G, u); for(j=0; j<G.vexnum; ++j) if(j!=k) closedge[j] = { u, G.arcs[k][j].adj }; closedge[k].lowcost = 0; for(i=1; i<G.vexnum; ++i){ k = minimun(closedge); printf(colsedge[k].adjvex, G.vexs[k]); closedge[k].lowcost = 0; for(j=0; j<G.vexnum; ++j) if(G.arcs[k][j].adj < closedge[j].lowcost) colsedge[j] = { G.vexs[k], G.arcs[k][j].adj }; } }

  14. Prim()算法中有两重for循环,所以时间复杂度为O(n2)。与网中的边数无关,适用于求边稠密的网的最小生成树。

  15. 克鲁斯卡尔算法(避圈法)Kruskal于1956年提出 假设G=(V,E)是一个具有n个顶点的带权连通无向图,T=(U,TE)是G的最小生成树,则构造最小生成树的步骤如下: Step1:T=(V,{}),有n个顶点而无边的非连通图; Step2:在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入TE;否则舍弃, 而选择下一条代价最小的边; Step3: 若T中有n-1条边,结束;否则转step2. 从一个零图开始,克鲁斯卡尔算法逐步增加生成树的边, 与普里姆算法相比,可称为“加边法”。

  16. 克鲁斯卡尔算法求解最小生成树的过程

  17. 为了简便,在实现克鲁斯卡尔算法Kruskal()时,参数E存放图G中的所有边,假设它们是按权值从小到大的顺序排列的。n为图G的顶点个数,e为图G的边数。为了简便,在实现克鲁斯卡尔算法Kruskal()时,参数E存放图G中的所有边,假设它们是按权值从小到大的顺序排列的。n为图G的顶点个数,e为图G的边数。 typedef struct { int u; /*边的起始顶点*/ int v; /*边的终止顶点*/ int w; /*边的权值*/ } Edge;

  18. void Kruskal(Edge E[], int n) { int i,j,k; int vset[MAXV]; for (i=0;i<n;i++) vset[i]=i; k=1; j=0; while (k<n) { /*生成的边数小于n时循环*/ if (vset[E[j].u] != vset[E[j].v]) { printf("(%d,%d):%d\n",E[j].u,E[j].v,E[j].w); k++; for (i=0;i<n;i++) /*两个集合统一编号*/ if (vset[i]==vset[E[j].v]) vset[i]=vset[E[j].u]; } j++; /*扫描下一条边*/ } }

  19. 完整的克鲁斯卡尔算法应包括对边按权值递增排序,上述算法假设边已排序的情况下,时间复杂度为O(n2)。完整的克鲁斯卡尔算法应包括对边按权值递增排序,上述算法假设边已排序的情况下,时间复杂度为O(n2)。 如果给定的带权连通无向图G有e条边,n个顶点,采用堆排序(在第10章中介绍)对边按权值递增排序,那么用克鲁斯卡尔算法构造最小生成树的时间复杂度降为O(eloge)。 由于它与n无关,只与e有关,所以说克鲁斯卡尔算法适合于求边稀疏的网的最小生成树。

  20. 7.5 有向无环图及其应用 1. 拓扑排序(Topological Sort) 设G=(V,E)是一个具有n个顶点的有向图,V中顶点序列v1,v2,…,vn称为一个拓扑(有序)序列,当且仅当该顶点序列满足下列条件:若<vi,vj>是图中的弧(即从顶点vi到vj有一条路径),则在序列中顶点vi必须排在顶点vj之前。 在一个有向图中找一个拓扑序列的过程称为拓扑排序。

  21. 拓扑序列: C1, C2, C3, C4, C5, C8, C9, C7, C6。 拓扑序列: C1, C2, C3, C8, C4, C5, C9, C7, C6 。 用顶点表示活动,用弧表示活动间的优先关系的有向图,称为顶点表示活动的网(Activity On Vertex Network),简称为AOV-网。

  22. 如何进行拓扑排序? • 方法一:(从图中顶点的入度考虑) • 从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。 • 从网中删去该顶点和所有以它为尾的弧; • 重复上述两步,直到图全部顶点输出;或当前图中不再存在没有前驱的顶点。 v1,v6,v4,v3,v2,v5 或 v1,v3,v2,v6,v4,v5

  23. 方法二:(从图中顶点的出度考虑,得到逆拓扑序列)方法二:(从图中顶点的出度考虑,得到逆拓扑序列) • 从有向图中选择一个出度为0的顶点并且输出它。 • 从网中删去该顶点和所有以它为头的弧; • 重复上述两步,直到图全部顶点输出;或当前图中不再存在出度为0的顶点。 方法三:当有向图中无环时,利用深度优先遍历进行拓扑排序 从某点出发进行DFS遍历时,最先退出DFS函数的顶点即出 度为0的顶点,是拓扑序列中最后一个顶点。按退出DFS函数的 先后记录下来的顶点序列即为逆拓扑序列。 问题:判定一个图是否有圈(回路)的方法?

  24. Status TopologicalSort( ALGraph G) { int St[MAXV], top=-1; /*栈St的指针为top*/ FindInDegree(G, indegree); for (i=0; i<G.vexnum; i++) if (! indegree[i]) { top++; St[top]=i; } count = 0; while (top>-1) { /*栈不为空时循环*/ i=St[top]; top--; printf("%d ",i); ++count; for( p=G.vertices[i].firstarc; p; p=p->nextarc){ k = p->adjvex; adj[j].count--; if ( !(--indegree[k])) { top++; St[top]=k; } } } if(count<G.vexnum) return ERROR; else return OK; }

  25. void FindInDegree( ALGraph G, int *indegree)  { int i; ArcNode *p;  for(i=0; i<G.vexnum; i++) indegree[i] = 0;  for(i=0; i<G.vexnum; i++) { p=G.vertexes[i].firstarc;  while(p! =NULL) { indegree[p->adjvex]++;  p = p->nextarc;  } //while } // for }

  26. 作业: 7.5 7.7

More Related