别样数据结构与算法学习(五)图

时间:2021-03-18 21:45:17

1-图的定义

小结

       图是一种扩展的树结构,每个结点可以指向任意的其它结点

       链表是特殊的树结构,树是特殊的图结构

       图这种数据结构常用于网络规划和路径规划等领域,GPS相关产品大量应用了图结构和图算法


2-图的存储

图的存储分两种:

       图的顺序存储,对于顺序存储来说我们如何表达顶点之间存在的联系呢?我们会借助邻接矩阵进行顺序存储。

       对于链式存储,多重链表,如何设计结点结构?我们会介绍几种链式存储结构,比如说邻接表,邻接多重表,十字链表

       我们先看顺序存储。

邻接矩阵

       设图G=(V, E)具有n(n>=1)个顶点v1,v2,...,vn和m条边或弧e1,e2,...,em,则G的邻接矩阵是V*V阶矩阵,记为A(G)。

       邻接矩阵存放n个顶点信息和n^2条边或弧信息

       其每一个元素aij定义为:

              aij = 0 顶点vi与vj不相邻接     1  顶点vi与vj相邻接

       优点:

              1,容易判断任意两个顶点之间是否有边或弧

              2,容易求取各个顶点的度。

       无向图可以采用压缩存储方式

邻接表

       对图中每一个顶点建立一个单链表,指示与该顶点关联的边或出弧。

        头结点                                                表结点

       vexinfo | firstarc                      adjvex | arcinfo | nextarc

       vexinfo:顶点的信息              adjvex:邻接顶点位置

       firstarc:第一条关联边结点 arcinfo:边的信息

       nextarc:下一条关联边结点

       需要多少存储空间?

       V+2E

十字链表

       将有向图的邻接表和逆邻接表结合在一起,就得到了有向图的另一种链式存储结构——十字链表。

                    头结点                                                    表结点

       vexinfo | firstin | firstout              tailvex | headvex | arcinfo | tnext | hnext

       vexinfo:顶点的信息                 tailvex:弧尾顶点位置

       firstin:第一条关联入弧结点      headvex:弧头顶点位置

       firstout:第一条关联出弧结点   arcinfo:弧的信息

                                                         tnext:弧尾相同的下一条弧

                                                          hnext:弧头相同的下一条弧

邻接多重表

       邻接表是无向图的一种很有效的存储结构,在邻接表中容易求得顶点和边的各种信息;

       但在邻接表中,每一条边都有两个结点表示,因此在某些对边进行的操作(例如对搜索过到边做标记)中就需要对每一条边处理两遍;

       故引入邻接多重表实现无向图的存储结构。

       邻接多重表的结构与十字链表相似

                 头结点                                                        表结点

       vexinfo | firstedge                        mark | einfo | ivex | inext | jvex | jnext

       vexinfo:顶点的信息                 mark:标志域

       firstedge:第一条关联边结点     einfo:边的信息

                                                           ivex:边的第一个顶点位置

                                                        inext:顶点i的下一条关联边

                                                           jvex:边的另一个顶点位置

                                                        jnext:顶点j的下一条关联边


3-图的遍历

定义

       从图中的某一顶点出发,沿着一些边访问图中所有的顶点,使得每个顶点仅被访问一次。

分类

       深度优先搜索 DFS (Depth First Search)

       广度优先搜索BFS(Breadth First Search)

深度优先遍历

算法描述

       访问起始顶点v

       当v还有邻接顶点未访问时

              -深度遍历未访问过的邻接顶点w

       当v的所有邻接顶点都被访问时

              -若图中所有顶点均已访问

                     》算法结束

              -若图中还有未访问的顶点

                     》以未访问顶点作为起始顶点深度遍历

       仙剑(走迷宫)

       其实就是我们以前接触过的二叉树的先序遍历,使用递归

广度优先遍历

算法描述

       1,访问起始顶点v0

       2,依次访问v0的各个邻接点v01,v02,...,v0x

       3,假设最近一次访问的顶点依次为vi1,vi2,...,viy,则依次访问vi1,vi2,....,viy的未被访问的邻接点

       4,重复3,直到所有顶点均被访问

       其实就是我们以前接触过的二叉树的层次遍历,需要借助队列

小结

       广度优先遍历与深度优先遍历是图结构的基础算法,也是其它图算法的基础


4 - 最小连通网

运营商的挑战

       在下图标出的城市间架设一条通信线路

要求

       任意两个城市间都能够通信

       将架设成本降至最低

问题的提出

       如何在图中选择n-1条边使得n个顶点间两两可达,且这n-1条边的权值之和最小?

目标

       必须使用且仅使用该网络中的n-1条边来联结网络中的n个顶点;

       不能使用产生回路的边;

       各边上的权值的总和达到最小

Prim算法

基本思想

       从图N={V, E}中选择某一顶点u0进行标记,之后选择与它关联的具有最小权值的边(u0, v),并将顶点v进行标记

       反复在一个顶点被标记,而另一个顶点未被标记的各条边中选择权值最小的边(u, v),并将未标记的顶点进行标记

       如此继续下去,直到图中所有顶点都被标记为止

Kruskal算法

基本思想:

       对于n个顶点的图G={V, E}

       构造一个只有n个顶点,没有边的图G'={V, O}

       在E中选择一条具有最小权值的边,若该边的两个顶点不构成回路,则将此边加入到T中;否则将此边舍去,重新选择一条权值最小的边

       如此重复下去,直到所有顶点都连通为止

小结

       Prim算法是针对顶点展开的,适合于边的数量较多的情况,算法复杂度是邻接矩阵:O(v2)                 邻接表:O(elog2v)

       Kruskal算法是针对边展开的,适合于边的数量较少的情况,算法复杂度是O(elog2e)


5 - 最短路径

问题分析

       问题的提出:给定一个带权有向图D与源点v,求v到D中其它顶点的最短路径。限定各边上的权值大于0。

       解决思路:Dijkstra提出按路径长度的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条路径,再参照它求出长度次短的一条最短路径,依次类推,直到从顶点v到其它各顶点的最短路径全部求出为止。

算法精髓

       S集内的顶点是已经找到最短路径的顶点

       V0到w的最短路径只能通过S集内的顶点

       D[w]可能改变:

              if (D[u] + edge[u, w] < D[w]) {

                     D[w] = D[u] + edge[u, w];

              }

所有顶点之间的最短路径

问题的提法:已知一个各边权值均大于0的带权有向图,对每一对顶点 v_i != v_j,要求求出v_i与v_j之间的最短路径和最短路径长度。

方法:Dijkstra:把所有顶点作为源点,重复执行?

floyd算法本质和Dijkstra本质相同

Floyd算法基本思想

定义一个n阶方阵序列:

       A(-1),A(0),...,A(n-1)

其中:

       A(-1)[i][j] = Edge[i][j]

A(k)[i][j] = min{A(k-1)[i][j],A(k-1)[i][k]+A(k-1)[k][j]}

       k = 0,1,....,n-1

小结

       Dijkstra最短路径算法是基于递推的思想设计的

       未达顶点的最短路径一定是由已达顶点的最短路径求出

       Floyd最短路径算法只是Dijkstra最短路径算法的加强,其本质还是递推