数据结构复习(2)图

文章目录

    • 五、图
      • 5.1 图的类型
      • 5.2 名词解释
      • 5.3 图的存储结构
        • 5.3.1 邻接矩阵
        • 5.3.2 邻接表
      • 5.4 图的遍历
        • 5.4.1 深度优先搜索
        • 5.4.2 广度优先搜索
      • 5.5 最小生成树(连通性问题)
        • 5.5.1 相关概念
        • 5.5.2 最小生成树算法
      • 5.6 最短路径
        • 5.6.1 Dijkstra算法
        • 5.6.2 Floyd算法
      • 5.7 拓扑排序

五、图

图是一种多对多的关系。

图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为: G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。

5.1 图的类型

  • 无向图

    图中任意两顶点之间的边都是无向边,则该图为无向图(Undirected graphs)。一般使用小括号表示“()”。

  • 有向图

    图中任意两顶点之间的边都是有向边,则该图为有向图(Directed graphs)。一般使用尖括号表示“<>”。

  • 完全图

    • 无向完全图: 在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。(含有n个顶点的无向完全图有(n×(n-1))/2条边)
    • 有向完全图: 在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图。(含有n个顶点的有向完全图有n×(n-1)条边)

5.2 名词解释

  • 邻接

    无向图中若两个顶点之间有边,则称之为邻接。

    有向图中若存在边 ,则称v1和v2邻接(单向,视边的方向定)

  • 顶点Vi的度(Degree)是指在图中与Vi相关联的边的数量。对于有向图来说,有入度(In-degree)和出度(Out-degree)之分,有向图顶点的度等于该顶点的入度和出度之和。

  • 路径

    从顶点Vi出发有一组边可到达顶点Vj,则称顶点Vi到顶点Vj的顶点序列为从顶点Vi到顶点Vj的路径(Path)。

    出发点和结束点相同的称为环。序列中顶点不重复的路径称为简单路径。

  • 连通

    若从顶点Vi到顶点Vj有路径可达,则称两顶点连通。若图中任意俩顶点都连通,则称该图为连通图。

  • 有些图的边或弧具有与它相关的数字,这种与图的边或弧相关的数叫做权(Weight)。

5.3 图的存储结构

图的常用的存储结构有:邻接矩阵邻接链表十字链表邻接多重表边表,其中邻接矩阵和邻接链表是比较常用的表示方法。

5.3.1 邻接矩阵

邻接矩阵表示法(Adjacency Matrix),用两个数组来表示图,一个一维数组存储顶点信息,一个二数组存储图中边的信息。

具体表示如下图(图源百度百科)

数据结构复习(2)图_第1张图片

5.3.2 邻接表

邻接表是图的一种链式存储结构,邻接表由表头节点和表节点两部分组成,图中每个顶点均对应一个存储在数组中的表头节点。如果这个表头节点所对应的顶点存在邻接节点,则把邻接节点依次存放于表头节点所指向的单向链表中。

  • 无向图邻接表

数据结构复习(2)图_第2张图片

  • 有向图邻接表

数据结构复习(2)图_第3张图片

  • 带权图邻接表

数据结构复习(2)图_第4张图片

图源来自Google。

其他结构略。

5.4 图的遍历

本质上就是对每个顶点查找其邻接点的过程。时间复杂度由存储结构确定。邻接矩阵存储的遍历复杂度为O(n2),邻接表存储的遍历复杂度为O(n)。

5.4.1 深度优先搜索

基本思想:假定所有顶点均未被访问,随机访问一个顶点,然后访问该节点的任意一个邻接顶点,重复以上过程直到无未被访问过的邻接点,若仍有顶点未被访问,则从剩余顶点当中随机选择一个访问,重复上述操作,直至所有顶点均被访问。

深度优先搜索是一个递归的过程。

5.4.2 广度优先搜索

基本思想:假定所有顶点均未被访问,最忌访问一个顶点,然后依次访问该顶点的所有邻接顶点,然后依次访问这些邻接点的邻接点。并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。

广度优先搜索遍历图的过程是以v为起点,由近至远,依次访问和v有路径相通且路径长度为1,2…的顶点。

算法实现见后续篇章。

图篇参考文章:https://www.cnblogs.com/skywang12345/p/3711483.html, 在此基础上做了内容的增改。

5.5 最小生成树(连通性问题)

连通图在遍历当中只用从一个顶点开始遍历就能走完全图,而非连通图则需要通过多个顶点才能够遍历全部。非连通图每一次遍历的新起点恰好是其各个连通分量的顶点集。

5.5.1 相关概念

连通图: 在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。

强连通图: 在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。

连通网: 在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。

生成树: 一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。

最小生成树: 在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。

5.5.2 最小生成树算法

参考文章 勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175

Prim算法是以点为对象,挑选与点相连的最短边来构成最小生成树。而Kruskal算法是以边为对象,不断地加入新的不构成环路的最短边来构成最小生成树。

Kruskal涉及大量对边的操作,所以它适用于稀疏图;普通的prim算法适用于稠密图,但堆优化的prim算法更适用于稀疏图,因为其时间复杂度是由边的数量决定的

  • Prim算法

    每一步骤选择相邻权重最小边的点加入集合,算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。复杂度O(n2)

    1)以某一个点开始,寻找当前该点可以访问的所有的边;
    2)在已经寻找的边中发现最小边,这个边必须有一个点还没有访问过,将还没有访问的点加入我们的集合,记录添加的边;
    3)寻找当前集合可以访问的所有边,重复2的过程,直到没有新的点可以加入;
    4)此时由所有边构成的树即为最小生成树

数据结构复习(2)图_第5张图片

  • Kruskal算法

    此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。复杂度为O(n log n)。

    1. 把图中的所有边按代价从小到大排序;

    2. 把图中的n个顶点看成独立的n棵树组成的森林;

    3. 按权值从小到大选择边,所选的边连接的两个顶点**ui,vi**应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。

    4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止

数据结构复习(2)图_第6张图片

算法实现见后续篇章。

5.6 最短路径

路径问题一般是基于带权有向图,称路径上第一个顶点为源点,路径最后一个顶点为终点。

路径长度最短的最短路径的特点:

  • 在这条路径上,必定只含一条弧,并且这条弧的权值最小。
  • 下一条路径长度次短的最短路径的特点:
  • 它只可能有两种情况: 或者是直接从源点到该点(只含一条弧); 或者是从源点经过顶点v1,再到达该顶点(由两条弧组成)。

问题解法:

  • 求从单个源点到其余各点的最短路径 — Dijkstra算法
  • 每一对顶点之间的最短路径 — Floyd算法
5.6.1 Dijkstra算法

时间复杂度为O(n2),Dijkstra算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

基本思想

  1. 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。

  2. 此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。

  3. 初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是”起点s到该顶点的路径”。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。

    参考 https://zhuanlan.zhihu.com/p/40338107

5.6.2 Floyd算法

时间复杂度为O(n3),弗洛伊德算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。

一般来说,从Vi到Vj的最短距离有两种,一是从Vi直接到Vj,二是两者之间经过若干其他顶点。我们可以这样来理解,当要求两个顶点之间的最短距离时,我们假设Dis(i,j)为Vi到Vj的最短距离,对于每一个Vk,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

从任意一条单边路径开始,边的长度为权值,若两顶点不邻接,权值设为无穷大;对每一对顶点Vi到Vj,看看是否存在一个Vk使得从Vi到Vk再到Vj比己知的路径更短,如果存在更新它。

5.7 拓扑排序

参考 https://www.jianshu.com/p/b59db381561a

由集合上的一个偏序得到该集合上的一个全序,这个操作称为拓扑排序。有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。

拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

基本思想

  1. 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。

  2. 从图中删除该顶点和所有以它为起点的有向边。

  3. 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

数据结构复习(2)图_第7张图片

上图为拓扑排序的简单示范。

你可能感兴趣的:(数据结构)