博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
每周一道数据结构(一)图
阅读量:7026 次
发布时间:2019-06-28

本文共 5126 字,大约阅读时间需要 17 分钟。

图的定义


   图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条

边,就表示这两个顶点具有相邻关系。

  图分为两类,一个是有向图,即每条边都有方向,另一个是无向图,即每条边都没有方向。
 
 
相关问题

  • 图的遍历问题
  • 最小生成树问题
  • 单源最短路径问题
  • 拓扑排序问题
  • 关键路径

 

图的遍历方法


 

  和树的遍历类似,图的遍历也是从某个顶点出发,沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问。它是许多图的算法的基础。深度优先遍历和广度优先遍历是最为重要的两种遍历图的方法。它们对无向图和有向图均适用。

1.深度优先算法的思想:

    假设图中任何一点都没有访问过,这时可以选择任意一点作为出发点,类似于数的前序遍历。则具体步骤如下:

  首先访问顶点V,并标记为以访问过。从顶点V的所有邻接点中,搜索为访问过的点W,如果存在W,则把W当作上一步的V,重复上一过程。直到图中所有可到达的路径都被访问过停止。如果图中还有为放过的顶点,则继续冲这部分定点中,选择一个节点作为顶点重复此过程。

代码:

void DFSTraverse(ALGraph *G){    for(int i=0;i
n;i++) visited[i]=FALSE; //标志向量初始化 for(int i=0;i
n;i++) if(!visited[i]) //vi未访问过 DFS(G,i); //以vi为源点开始DFS搜索}//DFSTraversevoid DFS(ALGraph *G,int i){ visit(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

2.广度优先算法的思想:

  设图G的初态是所有顶点均未访问过。在图中任选一顶点V,则广度优先遍历的具体步骤为:

  首先访问顶点V,接着依次访问v的所有邻接点W1,W2,…,Wt,然后再依次访问与Wl,W2,…,Wt邻接的所有未曾访问过的顶点。依此类推,直至图中所有和顶点V有路径相通的顶点都已访问到为止。此时从V开始的搜索过程结束。

代码:

void BFS(ALGraph*G,int k){    CreateQueue Q; //须将队列定义中DataType改为int    EdgeNode *p;    InitQueue(&Q);//队列初始化     //访问源点vk    visit(G->adjlist[k].vertex);    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未访问过 visit(C->adjlistlp->adjvex].vertex); //访问vj visited[p->adjvex]=TRUE; EnQueue(&Q,p->adjvex);//访问过的vj人队 }//endif p=p->next;//找vi的下一邻接点 }//endwhile }//endwhile }//end of BFS

 

生成树和最小生成树


 

  如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树(SpanningTree)。生成树是连通图的包含图中的所有顶点的极小连通子图。图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树。

  对于连通的带权图(连通网)G,其生成树也是带权的。生成树T各边的权值总和称为该树的权,记作:

                                    这里:TE表示T的边集,w(u,v)表示边(u,v)的权。

     权最小的生成树称为G的最小生成树(Minimum SpannirngTree)。最小生成树可简记为MST。

MST的性质:

  设G=(V,E)是一个连通网络,U是顶点集V的一个真子集。若(u,v)是G中所有的一个端点在U(u∈U)里、另一个端点不在U(即v∈V-U)里的边中,具有最小权值的一条边,则一定存在G的一棵最小生成树包括此边(u,v)。

求MST的一般算法可描述为:

  针对图G,从空树T开始,往集合T中逐条选择并加入n-1条安全边(u,v),最终生成一棵含n-1条边的MST。

  当一条边(u,v)加入T时,必须保证T∪{(u,v)}仍是MST的子集,我们将这样的边称为T的安全边。

伪代码:

GenerieMST(G){
//求G的某棵MST T=NULL; //T初始为空,是指顶点集和边集均空 while( T未形成G的生成树 ){ 找出T的一条安全边(u,v);//即T∪{(u,v)}仍为MST的子集 T=T∪{(u,v)}; //加入安全边,扩充T } return T; //T为生成树且是G的一棵MST }

 

Prim算法与Kruskal算法:

Prim思想:

  首先定义一个空集合T,用来存放MST的边和顶点。从图中选取一个顶点u,找到与u相连接的一条最短的边(u,v)作为第一条边,把(u,v)边和顶点v加入到集合T中。接下来,我们要选择下一条最短边(轻边),要求这条边的起点在集合T中,终点在集合T外。再重复上面的过程,直到找到n-1条边为止。

  由于可能存在相同长度的轻边,这就导致了最小生成树不唯一。

 Prim伪码:

PrimMST(G,T,r){   //求图G的以r为根的MST,结果放在T=(U,TE)中    InitCandidateSet(…);//初始化:设置初始的轻边候选集,并置T=({r},¢)    for(k=0;k

  复杂度为O(n^2),与边数无关.

Kruskal思想:

  与Prim算法不同,它只考虑每次所选的边为剩下的边集合中最短的那条边。这样就导致了,在找到最后一条边之前,集合T始终是一个森林。

KruskalMST(G) {
//求连通网G的一棵MST T=(V,¢); //初始化,T是只含n个顶点不包含边的森林 依权值的递增序对E(G)中的边排序,并设结果在E[0..e-1]中 for(i=0;i

  复杂度0(elge),与边有关。

 

最短路径


 

   单源最短路径问题:已知有向带权图(简称有向网)G=(V,E),找出从某个源点s∈V到V中其余各顶点的最短路径。

1、边上权值相等的有向网的单源最短路径

     用求指定源点的BFS生成树的算法可解决。

2、迪杰斯特拉(Dijkstra)算法求单源最短路径

     由Dijkstra提出的一种按路径长度递增序产生各顶点最短路径的算法。

Dijkstra思想:

  设S为最短距离已确定的顶点集,V-S是最短距离尚未确定的顶点集。

  ①初始化
        初始化时,只有源点s的最短距离是已知的(SD(s)=0),故点集S={s},点集V-S为空。
  ②重复以下工作,按路径长度递增次序产生各顶点最短路径
       在当前点集V-S中选择一个最短距离最小的点来扩充点集S,以保证算法按路径长度递增的次序产生各顶点的最短路径。
       当点集V-S中仅剩下最短距离为∞的点,或者所有点已扩充到点集S时,s到所有顶点的最短路径就求出来了。
  注意:
     ①若从源点到某点的路径不存在,则可假设到该点的最短路径是一条长度为无穷大的虚拟路径。
     ②从源点s到终点v的最短路径简称为v的最短路径;s到v的最短路径长度简称为v的最短距离,并记为SD(v)。

Dijkstra(G,D,s){    //用Dijkstra算法求有向网G的源点s到各顶点的最短路径长度    //以下是初始化操作      S={s};D[s]=0; //设置初始的点集S及最短距离D      for( all i∈ V-S )do //对点集V-S中每个顶点i          D[i]=G[s][i]; //设置i初始的估计距离为w
//以下是扩充红点集 for(i=0;i
D[k]+G[k][j]) //新点k使原D[j]值变小时,用新路径的长度修改D[j], //使j离s更近。 D[j]=D[k]+G[k][j]; } }

3、floyd算法

  通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。

floyd思想: 

  1. 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
  2. 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。

伪码:

void floyd(A,D){   //初始化   D[u,v]=A[u,v]   //更新 For k:=1 to n      For i:=1 to n         For j:=1 to n            If D[i,j]>D[i,k]+D[k,j] Then                 D[I,j]:=D[I,k]+D[k,j];}

 

拓扑排序


  对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(TopoiSicai Order)的序列,简称拓扑序列

  拓扑排序方法分为两种:无前趋的顶点优先的拓扑排序方法无后继的顶点优先拓扑排序方法

1.无前趋的顶点优先的拓扑排序方法

   该方法的每一步总是输出当前无前趋(即人度为零)的顶点。

伪码:

NonPreFirstTopSort(G){
//优先输出无前趋的顶点 while(G中有人度为0的顶点)do{ 从G中选择一个人度为0的顶点v且输出之; 从G中删去v及其所有出边; } if(输出的顶点数目<|V(G)|) //若此条件不成立,则表示所有顶点均已输出,排序成功。 Error("G中存在有向环,排序失败!"); }

2.无后继的顶点优先拓扑排序方法

    该方法的每一步均是输出当前无后继(即出度为0)的顶点。对于一个DAG,按此方法输出的序列是逆拓扑次序

伪码:

NonSuccFirstTopSort(G){
//优先输出无后继的顶点 while(G中有出度为0的顶点)do { 从G中选一出度为0的顶点v且输出v; 从G中删去v及v的所有人边 } if(输出的顶点数目<|V(G)|) Error("G中存在有向环,排序失败!");}

 

本文 由  创作,采用 进行许可。欢迎转载,请注明出处:
转载自: 

你可能感兴趣的文章
无法打开登录所请求的数据库 "ASPState"。登录失败。 用户 'NT AUTHORITY/SYSTEM' 登录失败。...
查看>>
Windows Phone开发(47):轻松调用Web Service
查看>>
ExecuteScalar的学习日志
查看>>
解决 dotNetZip 解压乱码的问题,支持ZIP分卷解压缩
查看>>
每日英语:Who Needs to Know How to Code
查看>>
oracle11g重新安装oem
查看>>
initrd映像文档的作用和制作
查看>>
Windows Phone-框架结构和启动过程
查看>>
PHP抓取网络数据的6种常见方法
查看>>
android之RatingBar控件用法
查看>>
Cocos2dx Label
查看>>
SkipFish
查看>>
我的菜单在母版页,如何更改菜单点击后的效果
查看>>
积累的VC编程小技巧之树操作
查看>>
Oracle碎碎念~1
查看>>
服务器虚拟化ESXi 5.5安装过程
查看>>
推荐部署网站
查看>>
性能测试误区
查看>>
数据库字段为日期类型时
查看>>
C/C++产生随机数
查看>>