[数据结构]图(一)

来源:互联网 发布:像素骑士团合战数据 编辑:程序博客网 时间:2024/06/01 16:06

第七章 图

7.1 抽象数据类型图的定义

图的结构定义:图是由一个顶点集 V 和一个弧集 R构成的数据结构。

Graph = (V , R )其中,R={<v,w>| v,w∈V 且 P(v,w)}

<v,w>表示从 v 到 w 的一条弧,并称 v 为弧头,w 为弧尾。

 

由于“弧”是有方向的,因此称由顶点集和弧集构成的图为有向图。

例如:G1 = (V1, VR1)

其中

V1={A, B, C, D, E}

VR1={<A,B>, <A,E>,           

  <B,C>, <C,D>, <D,B>,

  <D,A>, <E,C> }

 

若<v, w>ÎVR 必有<w, v>ÎVR, 则称 (v,w) 为顶点v 和顶点 w 之间存在一条边。

由顶点集和边集构成的图称作无向图。

例如: G2=(V2,VR2)

V2={A, B, C, D, E, F}

VR2={(A,B), (A,E),

  (B,E), (C,D), (D,F),

  (B,F), (C,F) }

 

名词和术语

网、子图    

完全图、稀疏图、稠密图

邻接点、度、入度、出度

路径、路径长度、简单路径、简单回路

连通图、连通分量、强连通图、强连通分量

生成树、生成森林

 

弧或边带权的图分别称作有向网或无向网。

 

设图G=(V,{VR}) 和图 G’=(V’,{VR¢}),且 V’V, VR’VR,

则称 G’为 G 的子图。

假设图中有 n 个顶点,e 条边,则

含有 e=n(n-1)/2 条边的无向图称作完全图;

含有 e=n(n-1) 条弧的有向图称作有向完全图;

若边或弧的个数 e<nlogn,则称作稀疏图,否则称作稠密图。

假若顶点v 和顶点w 之间存在一条边,则称顶点v 和w 互为邻接点,

边(v,w) 和顶点v 和w 相关联。和顶点v 关联的边的数目定义为顶点v的度。

例如:

ID(B) = 3       ID(A) = 2

 

对有向图来说,顶点的出度: 以顶点v为弧尾的弧的数目;

顶点的入度: 以顶点v为弧头的弧的数目。

顶点的度(TD)=出度(OD)+入度(ID)

 

例如:

OD(B) = 1

ID(B) = 2

TD(B) = 3

设图G=(V,{VR})中的一个顶点序列{ u=vi,0,vi,1, …, vi,m=w}中,(vi,j-1,vi,j)ÎVR 1≤j≤m,则称从顶点u 到顶点w 之间存在一条路径。路径上边的数目称作路径长度。

简单路径:序列中顶点不重复出现的路径。

简单回路:序列中第一个顶点和最后一个顶点相同的路径。

如:长度为3的路径{A,B,C,F}

 

若图G中任意两个顶点之间都有路径相通,则称此图为连通图;

 

若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。

 

对有向图,若任意两个顶点之间都存在一条有向路径,则称此有向图为强连通图

否则,其各个强连通子图称作它的强连通分量。

 

假设一个连通图有 n 个顶点和 e 条边,其中 n-1 条边和 n 个顶点构成一个极小连通子图,称该极小连通子图为此连通图的生成树。

对非连通图,则称由各个连通分量的生成树的集合为此非连通图的生成森林。

 

基本操作

·结构的建立和销毁

·对顶点的访问操作

·插入或删除顶点

·插入和删除弧

·对邻接点的操作

·遍历

结构的建立和销毁

CreatGraph(&G, V, VR):   // 按定义(V, VR) 构造图

DestroyGraph(&G):    // 销毁图

对顶点的访问操作

LocateVex(G, u);  // 若G中存在顶点u,则返回该顶点在

               // 图中“位置” ;否则返回其它信息。

GetVex(G, v);       // 返回 v 的值。

PutVex(&G, v, value);   // 对 v 赋值value。

对邻接点的操作

FirstAdjVex(G, v);   // 返回 v 的“第一个邻接点” 。若该顶点

                //在 G 中没有邻接点,则返回“空”。

NextAdjVex(G, v, w);   // 返回 v 的(相对于 w 的) “下一个邻接

                   // 点”。若 w 是 v 的最后一个邻接点,则

                   // 返回“空”。

插入或删除顶点

InsertVex(&G, v);   //在图G中增添新顶点v。

DeleteVex(&G, v);  // 删除G中顶点v及其相关的弧。

插入和删除弧

InsertArc(&G, v, w);   // 在G中增添弧<v,w>,若G是无向的,

                   //则还增添对称弧<w,v>。

DeleteArc(&G, v, w);  //在G中删除弧<v,w>,若G是无向的,

                   //则还删除对称弧<w,v>。

遍   历

DFSTraverse(G, v, Visit());   //从顶点v起深度优先遍历图G,并对每

                       //个顶点调用函数Visit一次且仅一次。

BFSTraverse(G, v, Visit());   //从顶点v起广度优先遍历图G,并对每

                       //个顶点调用函数Visit一次且仅一次。

7.2  图的存储表示

一、图的数组(邻接矩阵)存储表示

定义:矩阵的元素为

 

无向图的邻接矩阵为对称矩阵

 

有向图的邻接矩阵为非对称矩阵

邻接矩阵表示法特点:

1)无向图邻接矩阵是对称矩阵,同一条边表示了两次;

2)顶点v的度:在无向图中等于二维数组对应行(或列)中1的个数;在有向图中, 统计第 i 行 1 的个数可得顶点 i 的出度,统计第 j 列 1 的个数可得顶点 j 的入度。

3)判断两顶点v、u是否为邻接点:只需判二维数组对应分量是否为1;

4)顶点不变,在图中增加、删除边:只需对二维数组对应分量赋值1或清0;

5)设存储顶点的一维数组大小为n(图的顶点数n), G占用存储空间:n+n2;G占用存储空间只与它的顶点数有关,与边数无关;适用于边稠密的图;

typedef struct ArcCell { // 弧的定义

     VRType  adj;    // VRType是顶点关系类型      // 对无权图,用1或0表示相邻否;

     // 对带权图,则为权值类型。

     InfoType  *info;  // 该弧相关信息的指针

} ArcCell,  

     AdjMatrix[MAX_VERTEX_NUM]

                                [MAX_VERTEX_NUM];

typedef struct { // 图的定义

     VertexType       // 顶点信息

                    vexs[MAX_VERTEX_NUM];

     AdjMatrix    arcs;      // 弧的信息                     

      int    vexnum, arcnum;   // 顶点数,弧数      

      GraphKind   kind;     // 图的种类标志             

  } MGraph;

网络的邻接矩阵

 

二、图的邻接表存储表示

 

同一个顶点发出的边链接在同一个边链表中,每一个链结点代表一条边(边结点), 结点中有另一顶点的下标 adjvex和指针 nextedge。

有向图的邻接表

 

可见,在有向图的邻接表中不易找到指向该顶点的弧。

有向图的逆邻接表


在有向图的邻接表中,对每个顶点,链接的是指向该顶点的弧。

邻接表表示法特点:

1)无向图邻接表边结点数是边数的两倍.

2)顶点vi的度:在无向图中等于第i个链表中的结点数;在有向图邻接表中,第i行的结点数等于顶点i的出度,在有向图逆邻接表中,第i行的结点数等于顶点i的入度。

3)在邻接表上容易找到任一顶点的第一个邻接点和下一个邻接点

4)设存储顶点的一维数组大小为n(图的顶点数n),G占用存储空间:n+e;G占用存储空间与它的顶点数和边数有关;适用于边稀疏的图;

弧的结点结构

 

typedef struct ArcNode {  

  int        adjvex;   // 该弧所指向的顶点的位置

  struct ArcNode  *nextarc;

                             // 指向下一条弧的指针

  InfoType   *info;   // 该弧相关信息的指针

} ArcNode;

顶点的结点结构

 

typedef struct VNode {

  VertexType  data;   // 顶点信息

  ArcNode  *firstarc;

                   // 指向第一条依附该顶点的弧

  } VNode, AdjList[MAX_VERTEX_NUM];

图的结构定义

typedef struct {  

     AdjList  vertices;

     int      vexnum, arcnum;

     int      kind;          // 图的种类标志

  } ALGraph;

三、有向图的十字链表存储表示 

 

typedef struct ArcBox { // 弧的结构表示

     int tailvex, headvex;   InfoType  *info;

     struct ArcBox  *hlink, *tlink;   

     } VexNode;

 

typedef struct VexNode { // 顶点的结构表示

     VertexType  data;

     ArcBox  *firstin, *firstout;   

} VexNode;

有向图的结构表示(十字链表)

typedef struct {

   VexNode  xlist[MAX_VERTEX_NUM];

             // 顶点结点(表头向量)

   int   vexnum, arcnum;

           //有向图的当前顶点数和弧数

} OLGraph;

 

四、无向图的邻接多重表存储表示

边的结构表示

typedef struct Ebox {

     VisitIf       mark;      // 访问标记

     int      ivex, jvex;

                    //该边依附的两个顶点的位置

     struct EBox  *ilink, *jlink;

     InfoType     *info;          // 该边信息指针

} EBox;

顶点的结构表示

typedef struct VexBox {

   VertexType  data;

   EBox  *firstedge; // 指向第一条依附该顶点的边

} VexBox;

无向图的结构表示

typedef struct {  // 邻接多重表

    VexBox  adjmulist[MAX_VERTEX_NUM];

     int   vexnum, edgenum;    

  } AMLGraph;

 

7.3 图的遍历

从图中某个顶点出发游历图,访遍图中其余顶点,并且使图中的每个顶点仅被访问一次的过程。

·深度优先搜索

·广度优先搜索

·遍历应用举例

一、深度优先搜索遍历图

连通图的深度优先搜索遍历

从图中某个顶点V0 出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到。

W1、W2和W3 均为 V 的邻接点,SG1、SG2 和 SG3 分别为含顶点W1、W2和W3 的子图。

 

访问顶点 V :

for (W1、W2、W3 )若该邻接点W未被访问,则从它出发进行深度优先搜索遍历。

从上页的图解可见:

1. 从深度优先搜索遍历连通图的过程类似于树的先根遍历;

2. 如何判别V的邻接点是否被访问?

解决的办法是:为每个顶点设立一个 “访问标志 visited[w]”。

例如:

 

void DFS(Graph G, int v) {

   // 从顶点v出发,深度优先搜索遍历连通图 G

    visited[v] = TRUE;   VisitFunc(v);

    for(w=FirstAdjVex(G, v);

             w!=0; w=NextAdjVex(G,v,w))

        if (!visited[w])  DFS(G, w);     

              // 对v的尚未访问的邻接顶点w

              // 递归调用DFS

} // DFS

非连通图的深度优先搜索遍历

首先将图中每个顶点的访问标志设为 FALSE,  之后搜索图中每个顶点,如果未被访问,则以该顶点为起始点,进行深度优先搜索遍历,否则继续检查下一顶点。

例如:

 

void DFSTraverse(Graph G,

                             Status (*Visit)(int v)) {

   // 对图 G 作深度优先遍历。

  VisitFunc = Visit;   

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

     visited[v] = FALSE; // 访问标志数组初始化

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

     if (!visited[v])  DFS(G, v);

              // 对尚未访问的顶点调用DFS

}

二、广度优先搜索遍历图

对连通图,从起始点V到其余各顶点必定存在路径。

其中,V->w1, V->w2, V->w8 的路径长度为1;

V->w7, V->w3, V->w5的路径长度为2;

V->w6, V->w4 的路径长度为3。

 

从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问过的邻接点,之后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有和V0有路径相通的顶点都被访问到。

 若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

 

 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 尚未访问

           

    }

  } // BFSTraverse

visited[u] = TRUE;  Visit(u);    // 访问u

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

while (!QueueEmpty(Q))  {

   DeQueue(Q, u);        

                          // 队头元素出队并置为u

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

                          w=NextAdjVex(G,u,w))

      if ( ! visited[w])  {

         visited[w]=TRUE;  Visit(w);

         EnQueue(Q, w); // 访问的顶点w入队列

      } // if

} // while

图的连通性问题

连通分量 (Connected component)当无向图为非连通图时, 从图中某一顶点出发, 利用深度优先搜索算法或广度优先搜索算法不可能遍历到图中的所有顶点, 只能访问到该顶点所在的最大连通子图(连通分量)的所有顶点。

 

· 若从无向图的每一个连通分量中的一个顶点出发进行遍历,   可求得无向图的所有连通

   分量。

· 求连通分量的算法需要对图的每一个顶点进行检测:若已被访问过,则该顶点一定是落

  在图中已求得的连通分量上;若还未被访问,则从该顶点出发遍历图,可求得图的另一个

  连通分量。

·对于非连通的无向图,所有连通分量的生成树组成了非连通图的生成森林。

 

非连通无向图

DFS 访问序列:

ALMJBFC        

DE      

GKHI

 

 

 

0 0
原创粉丝点击