常见的数据结构-图
- 常见的数据结构-图
- 概念
- 分类与基本实现和特点
- 无向图
- 有向图
- 完全图
- 带权图
- 图的存储
- 1.邻接矩阵
- 2.邻接表
- 图的常见算法
- 图的遍历:DFS&BFS
- DFS:
- BFS:
- 单源最短路径(迪杰斯特拉Dijkstra 算法)
- 图论-拓扑排序
- 算法思想:
- 最小生成树
- 1.Kruskal算法(加边法)
- 算法步骤:
- 2.Prim算法(加点法)
- 算法步骤:
- 常见应用场景
常见的数据结构-图
概念
图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合
注意:顶点Vi的度(Degree)是指在图中与Vi相关联的边的条数。
对于有向图来说,有入度(In-degree)和出度(Out-degree)之分,
有向图顶点的度等于该顶点的入度和出度之和
注意:有些图的边或弧具有与它相关的数字,这种与图的边或弧相关的数叫做权
(Weight)
分类与基本实现和特点
无向图
如果图中任意两个顶点之间的边都是无向边(简而言之就是没有方向的边),
则称该图为无向图(Undirected graphs)
有向图
如果图中任意两个顶点之间的边都是有向边(简而言之就是有方向的边),
则称该图为有向图(Directed graphs)
完全图
- 1.无向完全图
在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图(含有n个顶点的无向完全图有[nx(n-1)]/2条边) - 2.有向完全图
在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图(含有n个顶点的有向完全图有nx(n-1)条边) - 3.当一个图接近完全图时,则称它为稠密图(Dense Graph)
而当一个图含有较少的边时,则称它为稀疏图(Spare Graph)
带权图
如果图中每条边都有一个权重,则该图称为带权图
图的存储
1.邻接矩阵
存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息
不足:由于存在n个顶点的图需要n*n个数组元素进行存储,当图为稀疏图时,使用邻接矩阵存储方法将会出现大量0元素,这会造成极大的空间浪费。
这时,可以考虑使用邻接表表示法来存储图中的数据
2.邻接表
邻接表由表头结点和表结点两部分组成,图中每个顶点均对应一个存储在数组中的表头结点。如果这个表头结点所对应的顶点存在邻接结点,则把邻接结点依次存放于表头结点所指向的单向链表中
图的常见算法
图的遍历:DFS&BFS
DFS:
- 1.从图中某个顶点Vo出发,首先访问Vo
- 2.访问结点Vo的第一个邻接点,以这个邻接点Vt作为一个新结点,访问Vt所有邻接点,直到以Vt出发的所有结点都被访问到
- 回溯到Vo的下一个未被访问过的邻接点,以这个邻接点为新结点
- 重复上述步骤,直到图中与Vo相通的所有结点都被访问到
- 3.若此时图中仍有未被访问的结点,则另选图中的一个未被访问的顶点作为起始点。重复深度优先搜索过程,直到图中的所有结点均被访问过
BFS:
- 1.从图中某个顶点Vo出发,首先访问Vo
- 2.依次访问Vo的各个未被访问的邻接点
- 3.依次从上述邻接点出发,访问他们的各个未被访问的邻接点。
始终保证一点:如果Vi在Vk之前被访问,则Vi的邻接点应在Vk的邻接点之前被访问。重复上述步骤,直到所有顶点都被访问到 - 4.如果还有顶点未被访问到,则随机选择一个作为起始点
重复上述过程,直到图中所有顶点都被访问到
提示:为了按照优先访问顶点的次序访问其邻接点,所以需要建立一个优先队列(先进先出)
单源最短路径(迪杰斯特拉Dijkstra 算法)
定一个起点S(源),求出其与所有顶点的最短路径。最短指的是权值之和最小
基本思想:
- 1.找到所有已知顶点(起始是只有源点S)
- 2.将所有已知顶点指向的所有未知顶点罗列出来
- 3.计算源点S到这些未知顶点的distance,找到新distance 最小的顶点X
- 4.只修改X的distance,并将X设为已知
- 5.回到第2步,若所有已知顶点的指向结点都已知,则结束
图论-拓扑排序
拓扑排序(Topological Sorting)是一个有向无环图(DAG)的所有顶点的线性序列
该序列必须满足下面两个条件:
1.每个顶点出现且只出现一次
2.若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点B的前面
注意:有向无环图才有拓扑排序,非DAG图没有拓扑排序一说
通常,一个有向无环图可以有一个或多个拓扑排序序列
算法思想:
- 1.从DAG图中选择一个没有前驱(即入度为0)的顶点并输出
- 2.从图中删除该顶点和所有以它为起点的有向边
- 3.重复以上步骤,直到当前图中不存在无前驱的顶点
最小生成树
图的生成树包含图的所有结点且仅有n-1边的子图
最小生成树是所有边的代价之和最小的生成树。
求最小生成树有以下两种算法
1.Kruskal算法(加边法)
此算法初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里
算法步骤:
(1)把图中的所有边按代价从小到大排序
(2)把图中的n个顶点看成独立的n棵树组成的森林
(3)按权值从小到大选择边,所选的边连接的两个顶点Ui、Vi,Ui、Vi应属于两棵不同的树,则成为最小生成树的一条边,并将这两棵树合并作为一棵树
(4)重复步骤(3),直到所有顶点都在一棵树内或者有n-1条边为止
2.Prim算法(加点法)
此算法每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大直至覆盖整个连通网的所有顶点
算法步骤:
(1)图的所有顶点集合为V;初始令集合u={s},v=V-u
(2)在两个集合u、v能够组成的边中,选择一条代价最小的边(uo,vo),加入到最小生成树中,并把vo并入到集合u中,Vo)
(3)重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止
由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息
常见应用场景
1.微博、微信等社交网络中的好友关系
2.地图导航、交通网络
3. 游戏地图、迷宫
4.计算机网络
5.人际关系推荐系统
6.知识图谱