图论专题总结
2016-07-06 20:34
211 查看
图论能解决很多问题,现在介绍一下关于图论的最小树问题。
1、 先要介绍两个基本的算法,这不是多此一举,正确的应用往往是建立在深刻理解之上的。
(1) Prim
将整个图分成两个集合,一个集合中的点已在最小生成树中,另一个集合中的点不在最小生成树中,每次将两集合的交叉边中最小的一条相连的不在生成树集合的点加入生成树集合中,直到加入了n-1条边为止。
显然复杂度与点的个数有关,为o(n2),必要时用堆优化。适用于点数较少的稠密图。代码略。
(2) Kruscal
将边排序,每次将最小的边的两端加入生成树集合,直到所有节点都在一个集合为止。
算法要点:Kruskal算法的最难点在于怎样判断加入边(x,y)后是否形成了环(生成树要保证无环)。
问题可化为: 判断边(x,y)的两个顶点x,y在图(实际是森林)mst中是否已经连通。如果已经连通,加入边将形成环;否则,不形成环。
并查集:连通点集之类问题,有高效算法---并查集。
除此之外,还有关于最短路径的问题。
先介绍三种适用范围不同的常用算法:
Dijkstra算法。这个算法可以求出图中某一个点到其它所有点的最短距离以及路径,但前提是图中没有权为负的边。它的大致思路是:每次找到当前距离最短的一个点,将它的距离固定下来,并用这个距离更新它的邻接点的距离,直至所有点的距离都被固定。采用最朴素实现的Dijkstra的时间复杂度是Θ(n^2)。有一种高效的实现方法是使用一个最小堆保存所有顶点的当前距离,但这造成了需要更新当前距离时的不便,导致了实际中编程的复杂性,一般不考虑使用,点数较少的时候使用。
SPFA算法。(我不打算提Bellman-Ford算法,因为完全可以把SPFA当作Bellman-Ford的一种优化、变形以及竞赛中的替代品。)同样是用来解决单源最短路,图中可以有负权的边。使用一个队列,首先使源点入队,然后每次出队一个顶点,用这个顶点的当前距离更新它的所有邻接点的距离,所有距离实际上被更新且未在队列中的点入队。重复以上过程直至队列空。另外,当一个点入队次数超过图的顶点数时,表明图中存在负权环。SPFA的最坏情况,也就是图中有负权环时的时间复杂度是Θ(n*E),但在实际应用中若没有负权环时会非常快,甚至可以认为大约与O(E)同阶。我在用到SPFA时一般都采用边表来来存边,这样可以最大限度发挥SPFA的优越性。点数较多而且边数较少时使用。竞赛中使用最频繁,往往考察对spfa算法的改造。或者多约束条件的spfa算法。
Floyd算法可以比较高效地求出图中所有顶点两两之间的最短距离,有负权边也没问题。它的基本思想是枚举每一个顶点,试图用“松弛操作”将它加到最短路径中去。具体的算法不太好用纯语言来描述,所以我就不描述了,保证看代码能一看就懂。它的一大优点是程序非常短且非常不容易写错。所以说,对于一些简单题,图的顶点比较少,特别是在还会有负权边的时候,哪怕需要求的是单源最短路,写一个Floyd也是明智的。Floyd的时间复杂度是雷打不动的Θ(n^3)。常常用作预处理,往往可以对方程进行改造,实现不同的任务要求。
常见的几个应用有:记录路径,记录路径数目,最小环。其他问题则需要具体分析
不管使用什么算法,如果要求得到最短路的具体路径,可以对每个顶点增加一个pre域(在Floyd中,pre需要是一个二维数组),以保存到达这个顶点的最短路的上一个顶点的编号,这样就可以沿着pre提供的信息,逆向找到整条最短路。
无向图最小生成树。
先注意,是无向图最小生成树。1、 先要介绍两个基本的算法,这不是多此一举,正确的应用往往是建立在深刻理解之上的。
(1) Prim
将整个图分成两个集合,一个集合中的点已在最小生成树中,另一个集合中的点不在最小生成树中,每次将两集合的交叉边中最小的一条相连的不在生成树集合的点加入生成树集合中,直到加入了n-1条边为止。
显然复杂度与点的个数有关,为o(n2),必要时用堆优化。适用于点数较少的稠密图。代码略。
(2) Kruscal
将边排序,每次将最小的边的两端加入生成树集合,直到所有节点都在一个集合为止。
算法要点:Kruskal算法的最难点在于怎样判断加入边(x,y)后是否形成了环(生成树要保证无环)。
问题可化为: 判断边(x,y)的两个顶点x,y在图(实际是森林)mst中是否已经连通。如果已经连通,加入边将形成环;否则,不形成环。
并查集:连通点集之类问题,有高效算法---并查集。
除此之外,还有关于最短路径的问题。
最短路
这是图论中最为精髓的部分了。先介绍三种适用范围不同的常用算法:
Dijkstra算法。这个算法可以求出图中某一个点到其它所有点的最短距离以及路径,但前提是图中没有权为负的边。它的大致思路是:每次找到当前距离最短的一个点,将它的距离固定下来,并用这个距离更新它的邻接点的距离,直至所有点的距离都被固定。采用最朴素实现的Dijkstra的时间复杂度是Θ(n^2)。有一种高效的实现方法是使用一个最小堆保存所有顶点的当前距离,但这造成了需要更新当前距离时的不便,导致了实际中编程的复杂性,一般不考虑使用,点数较少的时候使用。
SPFA算法。(我不打算提Bellman-Ford算法,因为完全可以把SPFA当作Bellman-Ford的一种优化、变形以及竞赛中的替代品。)同样是用来解决单源最短路,图中可以有负权的边。使用一个队列,首先使源点入队,然后每次出队一个顶点,用这个顶点的当前距离更新它的所有邻接点的距离,所有距离实际上被更新且未在队列中的点入队。重复以上过程直至队列空。另外,当一个点入队次数超过图的顶点数时,表明图中存在负权环。SPFA的最坏情况,也就是图中有负权环时的时间复杂度是Θ(n*E),但在实际应用中若没有负权环时会非常快,甚至可以认为大约与O(E)同阶。我在用到SPFA时一般都采用边表来来存边,这样可以最大限度发挥SPFA的优越性。点数较多而且边数较少时使用。竞赛中使用最频繁,往往考察对spfa算法的改造。或者多约束条件的spfa算法。
Floyd算法可以比较高效地求出图中所有顶点两两之间的最短距离,有负权边也没问题。它的基本思想是枚举每一个顶点,试图用“松弛操作”将它加到最短路径中去。具体的算法不太好用纯语言来描述,所以我就不描述了,保证看代码能一看就懂。它的一大优点是程序非常短且非常不容易写错。所以说,对于一些简单题,图的顶点比较少,特别是在还会有负权边的时候,哪怕需要求的是单源最短路,写一个Floyd也是明智的。Floyd的时间复杂度是雷打不动的Θ(n^3)。常常用作预处理,往往可以对方程进行改造,实现不同的任务要求。
常见的几个应用有:记录路径,记录路径数目,最小环。其他问题则需要具体分析
不管使用什么算法,如果要求得到最短路的具体路径,可以对每个顶点增加一个pre域(在Floyd中,pre需要是一个二维数组),以保存到达这个顶点的最短路的上一个顶点的编号,这样就可以沿着pre提供的信息,逆向找到整条最短路。
相关文章推荐
- [东莞市选2007]最难的问题_纪中1001_spfa
- Linux vmstat命令详解
- C语言中函数传递(实参 形参)
- 7_4_N题 Til the Cows Come Home 题解[poj 2387] (最短路)
- POJ 2182 Lost Cows
- POJ-2253 Frogger(Dijkstra)(Floyd)
- Java中对List集合的常用操作
- 建造者模式
- Linux开发工具(gcc gdb make shell)——GCC 三级优化
- lucene 目录结构简单介绍
- Android中Fragment的生命周期各状态和回调函数使用
- d393 【那些年遇到过的面试题】malloc 原理
- 常用的几种算法总结
- C#读取系统文件信息(二)——调API读取系统文件及系统盘Icon图标
- [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify uniq
- 偏向锁,轻量级锁,重量级锁
- 数据库索引的作用和优点缺点
- 王小川清华大学毕业典礼演讲:我也有过学渣经历(和时间做朋友,要和华军、天空这些下载站做合作推广)
- window平台安装MongoDB
- Guacamole环境搭建指南