题型总结
邻接矩阵
// times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。
int[][] adjoin = new int[n+1][n+1];
for (int[] arr : adjoin) {
Arrays.fill(arr,Integer.MAX_VALUE);
}
for (int[] arr : times) {
adjoin[arr[0]][arr[1]] = arr[2];
}
最短路径问题
- AAAA743. 网络延迟时间
求给定的k点到各个节点的最短距离,然后选择最大的那个。 - AAAA787. K 站中转内最便宜的航班
运用 Bellman Ford 求解有限制的最短路问题
最小生成树问题
-
AAAAA1584. 连接所有点的最小费用
kruskal算法+并查集 -
AAAA NC159 最小生成树
prime算法 构建邻接矩阵,用一个一维数组记录当前已经遍历的结点与未被纳入的结点的最短边,每次从这个一维数组中选出一个最短对应的结点,作为下一步要纳入的结点,同时遍历与这个结点连接且未被纳入的结点,更新一维数组,循环此过程直到所有的结点都被纳入。
这个一维数组理解为已纳入的结点到各个未纳入节点的最短边。
有向有环图
AAAA565. 数组嵌套
有向有环且不相交的图
图论算法
最短距离
Dijkstra算法
解决最短路径问题
用一个一维数组dis表示k到各个结点的最短路径长度。
用一个数组visited标记那些结点已经被遍历过了。
首先从dis中选出一个最短的路径,然后用这个最短路径去更新k到各个结点的最短路径长度。此过程循环n-1次就能找到k与n-1个结点的最短路径。
将节点分为两类,一类是已经确定了最短距离的,另一类是还没有确定的。
每次从还没有确定的节点里面挑选出距离K最短的节点(dis[i]最小的),改变遍历状态表示已经确定最短距离。
用dis[i]和邻接矩阵更新剩下的那些节点的最短节点。
已经确定了最短距离的结点肯定不会在被更新。如果K到节点A存在一个节点C使得dis[C] + AC之间的距离 < dis[A], 那肯定因为dis[C]小于dis[A]先遍历了C后遍历A,而不是先遍历A后遍历C,即矛盾了。
参考:最短路径——Dijkstra算法和Floyd算法
Floyd算法
是求任意两点的最短距离。有三层循环,最外面的一层是遍历所有结点,里面的两层是对邻接矩阵的遍历。
任意两点如A,B,其最短距离只有两种:1.A直接到B(邻接矩阵的初始值),2,A通过中间节点到B(A->…->B)。
(A->…->B)可以拆分成(A->…->C) + (C->…->B), C也可以继续拆分,直到没有中间节点。
最外层的循环就是用一个节点C尝试把(A->…->B)拆分成(A->…->C) + (C->…->B),如果算出的距离更短,就更新(A->…->B),最后用所有的节点挨个邻接矩阵的所有点都尝试更新一遍。
因为穷尽了所有情况,所以一定能够求出最短距离。
解决最短路径问题
我觉得Floyd算法的原理是这样的:A点到B点有多条路径,Floyd算法的作用是选出路径最短的那一条。
初始的邻接矩阵是一个点到另一个点的直接距离,如果要想找与k不直接连接的结点的距离,那最终的路径肯定是有中间节点的。用一个循环遍历所有的中间结点,然后用这个中间节点去更新k与各个结点的最短路径。
Flyod适合处理多源最短路径。
Bellman Ford
最小生成树
kruskal算法
解决最小生成树问题
从边的角度出发,每次加入能加入新结点的最短的边,直到遍历完所有的边。
不需要构建邻接矩阵。
设立数据结构edge,有三个属性,头结点,尾结点,路径长度。
构建并查集,给各个edge由小到大排序。
遍历edge集合,把当前的edge加入并查集,看头尾结点是不是在同一个集合里面,是的话就舍弃,不是就把长度加入道最终的结果中。
证明:
每次加入的边edge都是最短的,并且都是没有构成环路,没有构成回路意味着没有浪费边去连接已经加入的结点,即加入的边都是必要的。
N-1条边且不回环则这N个点一定被连接在一起。证明:如果N个结点没有连接到一起,那就会多出来一条边,多出来的边会连接已经接入的结点即构成回环,这与不回环的假设矛盾,所以N-1条边且不回环的N个结点一定是连通在一起,连通量只是1。
因为加入边都是按照由小到大的顺序加入,所以找不出比这N-1条边更短且连通了N个结点。
prime算法
解决最小生成树问题
从点的角度出发,每次选择与已加入结点连接的最短的未加入结点,直到所有的结点都加入了。
构建邻接矩阵。
一个一维数组dis表示已加入结点与未加入节点的最短距离。
一个一维数组visited表示哪些节点已经加入过了。
遍历dis选出与已加入结点连接的最短的未加入结点,把这条路径加入到最终结果中。然后用这个结点,更新所有dis,即未加入节点与已加入结点的最短路径。
循环n-1次便能找到连接n个结点的最小生成树了。
证明