什么是图

  • 图的定义

    “图”G可以表示为顶点和边的两个集合: G = ( V , E ) G=(V,E) G=(V,E)。每条边是一个顶点对 ( v , w ) ∈ E (v,w) \in E (v,w)E,并且 v , w ∈ V v,w \in V v,wV

  • 通常:
    ∣ V ∣ |V| V表示顶点的数量 ( ∣ V ∣ ≥ 1 ) (|V| \geq 1) (V1)
    ∣ E ∣ |E| E表示边的数量 ( ∣ E ∣ ≥ 0 ) (|E| \geq 0) (E0)

关于图的术语

  • 无向图(Undirected Graphs):边 ( v , w ) (v,w) (v,w)等同于边 ( w , v ) (w,v) (w,v)。用圆括号“ ( ) () ()”表示无向边。
    无向图
  • 有向图(Directed Graphs):边 < v , w > <v,w> <v,w>不同于边 < w , v > <w,v> <w,v>。用尖括号“ < > <> <>”表示有向边。有向边也称“弧(Arc)”。
    有向图
  • 邻接点:如果 ( v , w ) (v,w) (v,w) < v , w > <v,w> <v,w>是图中任意一条边,那么称 v v v w w w互为“邻接点(Adjacent Vertices)”。
  • 路径:图 G G G中从 v p v_p vp v q v_q vq的路径为 { v p , v i 1 , v i 2 , ⋯   , v i n , v q } \{ v_p,v_{i1},v_{i2}, \cdots ,v_{in},v_q \} {vp,vi1,vi2,,vin,vq},使得 ( v p , v i 1 ) , ( v i 1 , v i 2 ) , ⋯   , ( v i n , v q ) (v_p,v_{i1}),(v_{i1},v_{i2}), \cdots ,(v_{in},v_q) (vp,vi1),(vi1,vi2),,(vin,vq) < v p , v i 1 > , < v i 1 , v i 2 > , ⋯   , < v i n , v q > <v_p,v_{i1}>,<v_{i1},v_{i2}>, \cdots ,<v_{in},v_q> <vp,vi1>,<vi1,vi2>,,<vin,vq>都属于 E ( G ) E(G) E(G)
  • 路径长度:路径中边的数量。
  • 简单路径 v p , v q , v i 1 , v i 2 , ⋯   , v i n v_p,v_q,v_{i1},v_{i2}, \cdots ,v_{in} vp,vq,vi1,vi2,,vin都是不同顶点的路径。
  • 回路:起点和终点相同 ( v p = v q ) (v_p=v_q) (vp=vq)的路径。
  • 无环图:不存在任何回路的图。
  • 有向无环图:不存在回路的有向图,也称DAG(Directed Acyclic Graph)。
  • 简单图(Simple Graphs):没有重边和自回路的图。
    简单图
  • 无向完全图:在顶点数在给定为 n n n的情况下,边数达到最大的 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2条边的无向简单图。
    无向完全图
  • 有向完全图:在顶点数在给定为 n n n的情况下,边数达到最大的 n ( n − 1 ) n(n-1) n(n1)条边的有向简单图。
    有向完全图
  • 顶点的度(Degree):与顶点 v v v相关的边数。
  • 顶点的入度(In-Degree)和出度(Out-Degree):在有向图中,顶点 v v v的入度为以顶点 v v v为终点的路径的条数;顶点 v v v的出度为以顶点 v v v为起点的路径的条数。
    出度和入度
  • 稠密图和稀疏图:是否满足 ∣ E ∣ > ∣ V ∣ l o g 2 ∣ V ∣ |E|>|V|log_2{|V|} E>Vlog2V,作为稠密图和稀疏图的分界条件。
  • G G G的子图 G ′ G^{'} G:满足 V ( G ′ ) ⊆ V ( G ) V(G^{'}) \subseteq V(G) V(G)V(G)并且 E ( G ′ ) ⊆ E ( G ) E(G^{'}) \subseteq E(G) E(G)E(G)的图 G ′ G^{'} G称为图G的子图。
  • 无向图的连通性
    • 连通(Connected):如果无向图从一个顶点 v i v_i vi到另一个顶点 v j ( i ≠ j ) v_j(i≠j) vj(i=j)有路径,则称顶点 v i v_i vi v j v_j vj是连通的。
    • 连通图(Connect Graphs):无向图中任意两顶点都是连通的,则称该图是连通图。
    • 连通分量(Connected Component):无向图的极大连通子图(子图、连通、极大顶点数、极大边数)。
  • 有向图的连通性
    • 强连通图(Strongly Connected Graph):有向图中任意一对顶点 v i v_i vi v j ( i ≠ j ) v_j(i≠j) vj(i=j)均既有从 v i v_i vi v j v_j vj的路径,也有从 v j v_j vj v i v_i vi的路径,则称该有向图是强连通图。
    • 强连通分量(Strongly Connected Component):有向图的极大强连通子图称为强连通分量(子图、连通、极大顶点数、极大边数)。
  • 树(Tree):树是图的特例(无环的无向图)。
  • 生成树(Spanning Tree):所谓连通图G的生成树,是G的包含其全部n个顶点的一个极小连通子图。

图的表示

  1. 邻接矩阵(Adjacency Matrix)
    1. 定义

      图的逻辑结构分为两部分:V和E集合,其中,V是顶点,E是边。因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。

      邻接矩阵表示:
      A [ i ] [ j ] = { 1 ( v i , v j ) 或 < v i , v j > 为 图 G 的 边 0 或 ∞ ( v i , v j ) 或 < v i , v j > 不 为 图 G 的 边 A[i][j]= \begin{cases} 1 & (v_i,v_j)或<v_i,v_j>为图G的边\\ 0或\infty & (v_i,v_j)或<v_i,v_j>不为图G的边 \end{cases} A[i][j]={10(vi,vj)<vi,vj>G(vi,vj)<vi,vj>G

    2. 例:
      邻接矩阵表示
      邻接矩阵表示为:
      A [ i ] [ j ] = [ 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 1 0 ] A[i][j]=\left[ \begin{matrix} 0 & 0 & 1 & 1 & 0\\ 1 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1\\ 0 & 1 & 0 & 0 & 1\\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] A[i][j]=0100000010110001000100110

    3. 适用情况(就空间利用率而言):稠密图

  2. 邻接表(Adjacency List)
    1. 定义

      对于图G中的每个顶点 v i v_i vi,将所有邻接于 v i v_i vi的顶点 v j v_j vj链成一个单链表,这个单链表就称为顶点 v i v_i vi的邻接表,再将所有点的邻接表表头放到一个数组中,就构成了图的邻接表。

      邻接表结构

    2. 例:
      邻接表例子
      邻接表表示为:
      邻接表例子2

    3. 适用情况(就空间利用率而言):稀疏图

抽象数据类型描述

  1. 类型名称:图(Graph)
  2. 数据对象集:一非空的顶点集合Vertex和一个边集合Edge,每条边用对应的一对顶点表示。
  3. 操作集:对于任意的图 G ∈ G r a p h G \in Graph GGraph,顶点 v , v 1 v,v_1 v,v1 v 2 ∈ V e r t e x v_2 \in Vertex v2Vertex,以及任意访问顶点的函数 v i s i t ( ) visit() visit(),具体操作有:
    1. Graph Create( int VertexNum ):构造并返回一个空图;
    2. void Destroy(Graph G G G):释放图 G G G占用的存储空间;
    3. void InsertVertex( Graph G G G):返回一个在 G G G中增加了新顶点 v v v的图;
    4. void InsertEdge( Graph G G G,Edge E E E ):返回一个在 G G G中增加了新边 ( v 1 , v 2 ) (v_1,v_2) (v1,v2)的图;

代码实现

图的遍历

图遍历方法的分类主要在于访问图中节点的顺序,深度优先搜索以深度为优先顺序,而广度优先搜索以广度为优先顺序。

  1. 深度优先搜索(Depth First Search,简称DFS )

    1. 主要步骤(类似于树的先序遍历):

      1. 从任意一个节点 v 0 v_0 v0出发,访问它的邻接节点 v 1 v_1 v1,再从 v 1 v_1 v1出发,访问 v 1 v_1 v1的未被访问过的邻接节点……
      2. 当当前访问的节点 v n v_n vn没有邻接节点或者没有未被访问过的邻接节点时,退回 v n v_n vn的上一个节点 v n − 1 v_{n-1} vn1,搜索 v n − 1 v_{n-1} vn1的邻接节点,重复 v n v_n vn的操作。
      3. 当连通图中没有未被访问过的节点时,遍历结束。
    2. :走迷宫(遍历所有图中路径)
      DFS例子
      步骤
      DFS例子步骤
      节点访问顺序 0 − 2 − 6 − 4 − 3 − 5 − 7 − 1 0-2-6-4-3-5-7-1 02643571

    3. 代码实现

      void  DFS( Graph G,  int V )
      {
            /* 从第V个顶点出发递归地深度优先遍历图G */
            VertexType W;
            Visited[V] = TRUE;
            VisitFunc(V);      /* 访问第V个顶点 */
            for( W = FirstAdjV(G, V); W != NULL;  W = NextAdjV (G, V, W) )
            {
                if( Visited[W]==FALSE )
                DFS(G, W); /* 对V的尚未访问的邻接顶点W递归调用DFS */
            }
      }
      
      
  2. 广度优先搜索(Breadth First Search,简称BFS )

    1. 主要步骤(类似于树的层序遍历):

      1. 从任意一个节点 v 0 v_0 v0出发,依次访问它的邻接节点 v 1 , v 2 , ⋯   , v n v_1,v_2, \cdots ,v_n v1,v2,,vn;再依次访问 v 1 , v 2 , ⋯   , v n v_1,v_2, \cdots ,v_n v1,v2,,vn的所有未被访问的邻接节点;
      2. 重复步骤1,直到连通图中所有节点都被访问为止。
    2. :在下图中,从节点E出发BFS全图
      BFS例子
      步骤
      ( 1 ) (1) (1)
      BFS例子步骤1
      ( 2 ) (2) (2)
      BFS例子步骤2
      ( 3 ) (3) (3)
      BFS例子步骤3
      ( 4 ) (4) (4)
      BFS例子步骤4
      节点访问顺序 E − A − F − H − B − D − G − C E-A-F-H-B-D-G-C EAFHBDGC

    3. 代码实现

      void BFS(Graph G)
      {
          /* 按广度优先遍历图G。使用辅助队列Q和访问标志数组Visited */
          Queue *Q=NULL;
          VertexType U,V,W;
          for(U = 0; U < G.NV; U++)
          {
              Visited[U]=FALSE;
          }
          Q = CreatQueue( MaxSize ); /* 创建空队列Q */
      
          for(U = 0; U < G.NV; U++)
          {
              /* 从U开始进行BFS */
              /* 若U尚未访问 */
              if(Visited[U]==FALSE)
              {
                  Visited[U] = TRUE;
                  VisitFunc(U);        /* 访问U */
                  AddQ (Q, U);        /* U入队列 */
                  while(!IsEmptyQ(Q))
                  {
                      V = DeleteQ( Q );  /*  队头元素出队并置为V */
                      for( W = FirstAdjV(G, V);  W;  W = NextAdjV(G, V, W) )  /*出队列前,把它的未访问过的邻接点入队列*/
                      {
                          if(Visited[W]==FALSE)
                          {
                              Visited[W] = TRUE;
                              VisitFunc (W);      /* 访问W */
                              AddQ (Q, W);
                          }
                      }
                  }
              }
          }
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值