图的相关算法
图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
图论,是ACM程序设计这门课的最后一个专题,我觉得也是最难的一个专题,上述所提及的算法,只是图论中算法的极小一部分,图论的综合性比较强,比如有的图论题甚至会用到贪心或搜索的思想,所以遇到具体的问题应该具体的对待。
图论题的另一个特点是模板性非常强,对于常用的算法,只要将其编写成函数,遇到同类问题,只要将现成的函数复制进去,并且修改一下mian()函数和输入输出格式,就可以正确的得出结论。
这里主要就是学了并查集和最小生成树和最短路,最小生成树包括prim算法和kruskal算法最短路常用Dijkstra算法和Bellman-Ford算法
并查集
一,常见两种操作:
合并两个集合
查找某元素属于哪个集合
思想:每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快
步骤:
第一步,找到根结点
第二步,修改查找路径上的所有节点,将它们都指向根结点
(1)Prim算法:
基本思想:
任取一个顶点加入生成树;
在那些一个端点在生成树里,另一个端点不在生成树里的边中,取权最小的边,将它和另一个端点加进生成树。
重复上一步骤,直到所有的顶点都进入了生成树为止。
Prim算法一般适用于稠密图。因为prim算法是针对于结点的算法,边的多少与算法的代价关系不太大。
(2)kruskal算法
kruskal算法的基本思想:
对所有边从小到大排序;
依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;
直到生成树中有了n-1条边,即告终止。
算法的时间复杂度O(eloge)
将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边。
最终得到的结果就是最小生成树。
一般和并查集一块使用。
Kruskal算法一般用于稀疏图,kruskal是针对于边的算法,所以边越少越好。
并查集和最小生成树一般是用于将图连通,求最小代价了或者求最少需要修几条路。这一部分就在那一直修路。
(3)Dijkstra算法
把顶点集合V分成两组:
(1)S:已求出的顶点的集合(初始时只含有源点V0)
(2)V-S=T:尚未确定的顶点集合
将T中顶点按递增的次序加入到S中,保证:
(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度
(2)每个顶点对应一个距离值 S中顶点:从V0到此顶点的长度 T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度
依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和
求最短路径步骤
算法步骤如下:
G={V,E}
1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值 若存在
2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中
3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值 重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止
(4)Bellman-Ford算法:
Bellman-Ford算法是求含负权图的单源最短路径算法,效率很低,但代码很容易写。其原理为持续地进行松弛(原文是这么写的,为什么要叫松弛,争议很大),在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。Bellman- ford算法有一个小优化:每次松弛先设一个标识flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出。Bellman-ford算法浪费了许多时间去做没有必要的松弛,而SPFA算法用队列进行了优化,效果十分显著,高效难以想象。SPFA还有SLF,LLL,滚动数组等优化。
算法描述:
1,.初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。;
注意Dijkstra算法不能处理带有负权值的情况,此时需要用Bellman-Ford算法;