目录
一、图的存储方式(千奇百怪)
1)邻接表
2)邻接矩阵
3)其他
4)推荐存储方式(代码)
二、图的遍历
(1)宽度优先遍历
(2)深度优先遍历
编辑 三、拓扑排序算法
四、kruskal与prim(针对无向图)
(1)kruskal算法
(2)prim算法
五、Dijkstra算法
一、图的存储方式(千奇百怪)
1)邻接表
2)邻接矩阵
3)其他
也可以用数组表示图:
Q:一个数组arr,存储一个没有环的特殊图,其每个位置上的数字代表其父节点(eg:arr[0] = 5表示0的父节点是5),以此类推可得到下面的图:
使用小数组表示图:
一个数组中每一个位置都存放着一个数组,它依次存储【权重,起始点,中止点】,因此 [3, 0, 2] 就代表着有一条权重为3,从0开始,指到2的边,其余以此类推:
由于表达图的方式 千 奇 百 怪 ,所以推荐:
(1)选择一种习惯的图表示方法,用该种表示方式来实现所有的图的算法;
(2)想办法将题中所给出来的图转化为自己熟悉的图的表示方法,再运用写好的算法即可!
4)推荐存储方式(代码)
一种供参考的图的存储方式:
转化示例:
二、图的遍历
(1)宽度优先遍历
区别二叉树的宽度优先遍历,因为图中可能含有环!
仍然借助队列实现(二叉树的宽度优先遍历也是借助队列):
结合画图理解:
tips:若需要进一步减少常数级别的耗时,可以采用数组来替换哈希表,因为数组的查询比哈希表更快一些;
(2)深度优先遍历
借助栈和哈希表来实现:
结合下图来理解:
栈中保存的是DFS的路线!

三、拓扑排序算法
适用范围:要求有向图,且有入度为0的节点,没有环。
常用环境:编译顺序
如下图所示,假设编译文件A需要先编译文件BCD,编译文件B又需要先编译文件CDE,请问应该以什么样的顺序编译整个文件?
算法思路:
1、先找到一个入度为0的点,记录该点,并删除其“影响”(此处指有向边);
2、重复上述过程直到没有未记录的节点;
四、kruskal与prim(针对无向图)
作用:生成最小生成树(在保证连通性的情况下,确保边的权值总和最小);
(1)kruskal算法
从“ 边 ”的角度出发考虑,对于所有边,按照权重排序;依次连线上最小的边,若会形成环,则去掉这条边。
检查是否形成环会用到“并查集”的概念,该部分实现将会在后续课程涉及。
例1:
例2:是否成环 == in节点和out节点是否属于同一个集合中
并查集的简单替换:(比并查集慢)
思路:定义一个setMap,里面存储着每个节点和它所属的集合;为主函数提供两个方法——一个用于判断from和to节点是否处于同一个集合中,另一个用于实现合并集合的操作。
有了上述的并查集结构后,我们可以实现k算法:
(2)prim算法
从“ 点 ”的角度出发考虑,从任意的点开始,标记该点使用过,对于该点可达的边标记为解锁状态;选择当前解锁边中两侧点不全出现过且权值最小的一条,将其标记为使用过,并标记该边连接的另一个节点为使用过;重复上述直到用过所有点。
例:假设从A开始,解锁AB6、AC1、AD5三条边;选择AC1边,解锁CB5、CE6、CF4、CD5边;选择CD4边,解锁FE5、FD2边;选择FD2边,无需解锁;此时【ACFD】已经被使用,故只能选择CB5而非CD5、AD,解锁AB6、BE3;选择BE3,已经遍历所有点,p算法结束。
思考:为什么k算法需要并查集(集合查询结构)而p算法不需要?
k算法可能出现已经连成将节点两小片后再将两边相连成一大片的情况;而p算法总是选择相邻(已解锁)的边,使用哈希表即可。
代码实现:
结合图像理解:当A选择B时,AB、AC会被添加,但是由于A、B两点已经被食用过,所以会直接跳过AB边而寻找其他边!
五、Dijkstra算法
适用范围:可以有权值为负数的边,但是不能有累加和为负数的环!
作用:指定一个点,给出从该节点出发到所有其他点的最短距离。
算法流程:初始化一个距离表,将初始节点置为0,初始节点到其他节点的距离置为正无穷;每次从距离表中选取距离最近的节点,检查其所有边的权重,若从原点通过该点到达下一个点的距离更小,则更新距离表,否则不做处理;重复上述过程直至完成对所有点的遍历。
例:
代码实现:
堆优化思路:之前寻找最小值使用遍历的方法,所以较慢,所以考虑使用堆来存储数据;但是当权值更新后,不一定还能满足堆的顺序,此时需要对于其进行手动调整,只有这样是最快的方式。