1 / 45

图论(二)

图论(二). 李子星. 内容提要. 最短路径问题 单源最短路径问题 Dijkstra 算法 Bellman-Ford 算法 SPFA 算法 任意点对最短路径问题 Floyd 算法 最小生成树问题 无向图的最小生成树问题 Prim 算法 Kruskal 算法 有向图的最小生成树问题. 单源最短路径问题. 给定一个有 n 个点的带权有向图,问从指定的起点开始,到其他所有点的最短路径。. 单源最短路径问题. 2. 2. 3. 8. 1. 1. 3. 10. 1. 4. 3. 5. 4. 4. 1. 6. 5. 3.

Download Presentation

图论(二)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 图论(二) 李子星

  2. 内容提要 • 最短路径问题 • 单源最短路径问题 • Dijkstra算法 • Bellman-Ford算法 • SPFA算法 • 任意点对最短路径问题 • Floyd算法 • 最小生成树问题 • 无向图的最小生成树问题 • Prim算法 • Kruskal算法 • 有向图的最小生成树问题

  3. 单源最短路径问题 给定一个有n个点的带权有向图,问从指定的起点开始,到其他所有点的最短路径。

  4. 单源最短路径问题 2 2 3 8 1 1 3 10 1 4 3 5 4 4 1 6 5 3

  5. 单源最短路径问题 • 带权图中一个点到另一个点的最短路径一定唯一存在? • 有路径通达且没有“负权回路”则一定存在 • 不一定唯一 • 单源最短路径是否一定能构成一棵树? • 如果源点到其他所有点的最短路径都存在,则一定能找到一棵以指定的起点为根的树(边的方向总是从父亲指向儿子)

  6. 单源最短路径问题 2 2 3 8 1 1 3 10 1 4 3 5 4 4 1 6 5 -3

  7. Dijkstra算法 如果边权没有负值,可以用Dijkstra算法求解单源最短路径问题。

  8. Dijkstra算法 算法流程: • 初始化所有点的dist值为+∞,起点的dist值为0 • 从所有点中找到未被标记的dist值最小的点x,将x点标记 • 检查所有x引出的边,用x点的dist值与这条边的权值之和更新这条边指向的点的dist值(这个过程又被称为“松弛操作”) • 重复步骤2和3直到所有点都被标记

  9. Dijkstra算法 • 一个点的dist值就是“已知的从起点到达它的最短路径长度” • 如果记下每个点的dist值最后一次更新是由哪条边的松弛操作完成的,这条边就是“已知的从起点到达它的最短路径的最后一条边” • 一个点被标记也就意味着“这个点的dist值已经不可能再被更新”,即“起点到它的最短路径已经求得”

  10. Dijkstra算法 dist:+∞ dist:1 dist:+∞ dist:3 2 2 3 8 1 1 3 dist:0 10 1 4 dist:10 dist:+∞ dist:9 3 5 4 4 1 6 5 3 dist:2 dist:+∞ dist:3 dist:+∞ dist:5

  11. Dijkstra算法 朴素的Dijkstra算法的时间复杂度为O(n^2)。 如果所有点的dist值用一个堆来维护,“更新某个点的dist值”和“寻找dist值最小的点”就可以在O(logn)的时间复杂度内完成。 总时间复杂度就变成了O(elogn),其中e为图中的边数 实际上Dijkstra算法就是h函数为0的A*算法

  12. Bellman-Ford算法 如果边权可以为负值,那么可以使用Bellman-Ford算法求解单源最短路径问题。

  13. Bellman-Ford算法 算法流程: • 初始化所有点的dist值为+∞,起点的dist值为0 • 检查所有边uv,用u点的dist值与这条边的权值之和更新v点的dist值。即对所有的边执行一次松弛操作。若此步骤中某个点的dist值更新了,那么可以说此步骤是“有收获的” • 直到上一轮步骤2没有收获,或步骤2已经重复了n轮,否则再重复一次步骤2。若最后一轮步骤2仍旧是有收获的,那么说明起点能够到达一个负权回路,否则目标就已经达成了。

  14. Bellman-Ford算法 证明: • 如果起点能够到达某个负权回路,那么显然,步骤2总是会有收获。 • 如果起点无法到达一个负权回路,那么当所有点的dist值到达最低值时,步骤2就不再有收获了。 • 而一条最短路径一定没有环,即一条最短路径经过的边数一定不会超过n-1(经过了所有其他的点),所以n-1轮后,任何一个点作为终点的最短路径一定已经求得。

  15. Bellman-Ford算法 时间复杂度显然是O(ne)

  16. SPFA算法 稍微改进一下Bellman-Ford算法的过程,就得到SPFA(Shortest Path Faster Algorithm)算法。

  17. SPFA算法 算法流程: • 初始化所有点的dist值为+∞,起点的dist值为0,将起点加入一个“待检查点”的队列q • 从队列q的队首中取出一个节点x,对x引出的所有边执行松弛操作,若某个点的dist值被更新了且这个点不在队列q中,则将其加入队尾 • 重复步骤2直到某个点入队超过n次或队列为空

  18. SPFA算法 有理论能够证明,SPFA算法的平均时间复杂度为O(e) 。 但是有可能退化,最坏会到O(en)。 比较适合于稀疏图中求解单源最短路径。 如果不用队列,改用栈可以么?

  19. 单源最短路径问题

  20. 任意点对最短路径问题 给定一个有n个点的带权有向图,问任意两个点之间的最短路径长度。

  21. Floyd算法 代码如下(w最初是图的邻接矩阵,算法结束后是任意点对间的最短路径长度): for k=1 to n do for i=1 to n do for j=1 to n do if (w[i,k]+w[k,j]<w[i,j]) w[i,j]=w[i,k]+w[k,j]

  22. Floyd算法 for k=1 to n do // 此时的w[x,y]为:x到y的“中途只能 // 经过1..k-1这些点的”最短路径长度 // 下面的二重循环则是求“中途只能 // 经过1..k这些点的”最短路径长度 for i=1 to n do for j=1 to n do if (w[i,k]+w[k,j]<w[i,j]) w[i,j]=w[i,k]+w[k,j] 在w[i,j]被更新时记下其因哪个k被更新,将之存储在mid[i,j]中,则mid[i,j]就是i到j的最短路径中经过的编号最大的点,由此可以还原出任意点对间的具体的最短路径。

  23. Floyd算法 k 只经过1..k-1 只经过1..k-1 x y 只经过1..k-1

  24. Floyd算法 如果有负权边但没有负权回路,Floyd算法还是正确的么? 答案是依然正确。但是Floyd算法没法判断图中是否有负权回路存在。 时间复杂度显然是O(n^3)的。

  25. Floyd算法 如果图中有负权回路存在,Floyd算法最后得到的会是什么? 如果要求图(可能是无向图)的最小环,Floyd算法可以给你什么启示么?

  26. 最短路径问题 如果要求次短路径,甚至第k短路径呢? 如果这个图存在负权回路,但要求每个点不走两次的最短路径呢?

  27. 无向图的最小生成树问题 给定一个连通的带正权无向图,求此图的一个边集,要求: (1)将不在此边集的边全删除后,图依然连通; (2)边集中的边的权值之和最小。 易知这个边集一定构成一棵树,因此它又被称为“最小生成树”

  28. 无向图的最小生成树问题 1 3 2 4 1 3 2 3 1 7 2 4 5 4 3

  29. 无向图的最小生成树问题 • 将原图点集分成两个不相交的集合A和B • 原图中所有连接了A和B的边中权值最小的那条,一定可以作为最小生成树中的一条边

  30. 无向图的最小生成树问题 1 3 2 4 1 3 2 3 1 7 2 4 5 4 3

  31. Prim算法 算法流程: • 任选一个点s得到集合A={s},初始化dist[i]为w[s,i],link[i]=s • 在V-A中选取dist最小的点x,将x加入集合A,检查所有V-A中的点i,若w[x,i]<dist[i]则dist[i]=w[x,i]且令link[i]=x • 重复步骤2直到A=V,此时所有V-{s}中的点i与link[i]相连的边就构成了原图的一个最小生成树,此外dist[i]就是连接了i与link[i]的边的权值

  32. Prim算法 1 3 2 4 1 3 2 3 1 7 2 4 5 4 3

  33. Prim算法 时间复杂度是为O(n^2)的。 正确性依赖于之前说的原理。 虽然最小生成树本身是“无根树”,但link数组实际上对应了一棵以s为根的“有根树”,对于每个节点i,link[i]就是其在这棵树中的父节点。

  34. Kruskal算法 算法流程: • 将所有的边按权值从小到大排序,初始化A[i]={i}对所有的i,A[1..n]构成原图点集的一个划分 • 依边权从小到大依次检查每条边,若这条边连接的两个点不在一个划分块中,则标记这条边,然后将这两个划分块合并 • 重复步骤2,直到所有点都在一个划分块中为止。最后所有标记的边构成原图的一个最小生成树

  35. Kruskal算法 1 3 2 4 1 3 2 3 1 7 2 4 5 4 3

  36. Kruskal算法 原图点集的划分通常使用并查集来维护,所以Kruskal算法的时间复杂度就是O(eloge)。 显然,如果图是稀疏图, Kruskal算法效率应该好于Prim算法,而对于稠密图则相反。

  37. 无向图的最小生成树问题 如果图是动态变化的呢?比如图中的边会不断增加,或者不断减少。 如果要求的不是最小生成树,而是次小生成树,甚至第k小生成树呢? 如果每条边有两个参数c和w,要求选出的边集的c之和与w之和的比值最小呢? 这个又被称为“最优比率生成树”

  38. 有向图的最小生成树问题 给定一个连通的带正权有向图,并指定其中的某个点s,求此图的一个边集,要求: (1)将不在此边集的边全删除后,s到其他所有点都有路径。 (2)边集中的边的权值之和最小。 显然这个边集一定构成一棵以s为根的有根树,且这棵树中的边都是父节点指向儿子节点的。 这样的边集又被称为原图以s为根的“最小树形图”。

  39. 有向图的最小生成树问题 1 5 3 5 2 4 4 2 6 7 3 5 3

  40. 有向图的最小生成树问题 算法流程: • 对s外的每个点i,找到指向i的边中权值最小的那条,将这些边放入边集A0 • 若图中没有环且没有收缩点,则A0即为所求,算法结束;若有环则进入步骤3;若没有环但有收缩点,则进入步骤4 • 将每个环收缩为一个点,同时将这个环中的边从A0剔除,然后回到步骤1。收缩的具体操作如下: • 假设收缩产生的新点为P • 对于任意环外点x指向环内点y的权值为w的边xy,生成一条边xP,并且权值改为w减去环中指向y的边的权值,这条边实际上是原本的xy的“等价边”

  41. 有向图的最小生成树问题 S S 5 9 3 9-7 3 5-3 9 4 收缩 9-4 8-3 3 7 3 8 5 9-5 9 4-3 4 4 8-5 4 6 8 6

  42. 有向图的最小生成树问题 • 此时的A0(没环)对于现在的图(有收缩点)来说,就是一棵以s为根的最小生成树了,但由于存在收缩点,所以现在的图还不是原图,所以需要把收缩点展开,以还原到原图。展开后可能依然存在收缩点,需要重复此步骤直到没有收缩点为止,那时的A0即为所求 • 展开时需要将其在收缩时从A0中剔除的边加回到A0 • 对每个展开后的收缩点,其中肯定有一个点有两条边指向它,其中一条是所在收缩点外的某点指向它的,另一条则是收缩点内的某点指向它的,将后者从A0剔除

  43. 有向图的最小生成树问题 S S 5 9 3 9-7 3 5-3 9 4 展开 9-4 8-3 3 7 3 8 5 9-5 9 4-3 4 4 4 6 8-5 8 6

  44. 有向图的最小生成树问题 这个过程简而言之就是: • 找最小边 • 收缩 • 找最小边 • 收缩 • …… • 展开 • 展开 • ……

  45. 练习题 • POJ1502 • POJ3637 • POJ1511 • POJ1062 • POJ1860 • POJ1797 • POJ2253 • POJ1125 • POJ1258 • POJ2485 • POJ2075 • POJ3625 • POJ1679 • POJ3164

More Related