SDAU练习四总结

来源:互联网 发布:ubuntu查看ip 编辑:程序博客网 时间:2024/05/17 23:06

图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。

图论这个专题我感觉不是一般的难,虽然有模版有算法,解题难度还是很大。首先,老师讲的是图中边和点的存储方法,一个是利用二维数组的邻接矩阵的方式,但限制是点不能很多的,边可以稠密的图。另一个是邻接表,标准的应该是用指针式的链表存储,为了方便和运行速度,修改成结构体的数组来存储,运行速度要快,而且更加方便,它的限制就是边不要太多,点数可以很多的图。而后引出了并查集,就是相同的元素放在一个集合里,用数组表示。当然,这里面有合并集合和查找数值操作,都有固定的模版方法。

下面的才是主菜,一共两道。第一道主菜是最小生成树。最小生成树的定义:所有生成树中权值最小的一个边集T为最小生成树,确定树T的问题成为最小生成树问题。解决问题的方法有两种,一个是prim算法:任取一个顶点加入生成树;在那些一个端点在生成树里,另一个端点不在生成树里的边中,取权最小的边,将它和另一个端点加进生成树。重复上一步骤,直到所有的顶点都进入了生成树为止。一个是kruskal算法:对所有边从小到大排序;依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;直到生成树中有了n-1条边,即告终止。算法的时间复杂度O(eloge)。其中prim算法简单解释起来就是有2个集合,一个是已取的点集合,一个是未取的点集合,根据边的权值来从未取的集合中找点,而后将此点放到已取的集合中,直到未取的集合为空为止。看似很简单的操作,实际注意的细节有很多,最主要的是根据已取点集合的点与未取点集合的点的关系来找权值,而后才能进行合并操作。对于kruskal算法来说就比较单一了,首先就是看边的权值,让权值按从大到小排列,然后取相对最小的边,加入,判断,直到生成联通图为止。

第二道主菜就是最短路问题了,简单来说就是在地图上找路线,从出发点到达目的地的最佳的路线。一种算法是Dijkstra算法:设置一个集合S存放已经找到最短路径的顶点,S的初始状态只包含源点v,对vi∈V-S,假设从源点v到vi的有向边为最短路径。以后每求得一条最短路径v, …, vk,就将vk加入集合S中,并将路径v, …, vk , vi与原来的假设相比较,取路径长度较小者为最短路径。重复上述过程,直到集合V中全部顶点加入到集合S中。这算法和最小生成树的prim算法异曲同工,只是所求不同。Dijkstra算法有个很大的缺点就是如果权值是负值,那么就不能够实现,所以一定要因题选择。另一个算法是Bellman-Ford算法:Bellman-Ford算法构造一个最短路径长度数组序列dist 1 [u], dist 2 [u], …, dist n-1 [u]。其中:

dist1 [u]为从源点v到终点u的只经过一条边的最短路径长度,并有dist 1 [u] =Edge[v][u];

dist2 [u]为从源点v最多经过两条边到达终点u的最短路径长度;

dist3 [u]为从源点v出发最多经过不构成负权值回路的三条边到达终点u的最短路径长度;

……

distn-1 [u]为从源点v出发最多经过不构成负权值回路的n-1条边到达终点u的最短路径长度;

算法的最终目的是计算出dist n-1 [u],为源点v到顶点u的最短路径长度。

Dijkstra算法与Bellman算法的区别:Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,则之后不变了,修改的仅仅是源点到T集合中各顶点的最短路径长度。Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[ ],也就是说源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。

还有一种SPFA算法,算是Bellman算法的优化实现,一般比较常用:1.队列Q={s}2.取出队头u,枚举所有的u的临边.若d(v)>d(u)+w(u,v)则改进,pre(v)=u,由于d(v)减少了,v可能在以后改进其他的点,所以若v不在Q中,则将v入队。3.一直迭代2,直到队列Q为空(正常结束),或有的点的入队次数>=n(含有负圈)。

一般用于找负圈(效率高于Bellman-Ford),稀疏图的最短路。

这几种算法都有模版代码,但相对于前几讲的,还是很复杂的,尤其是根据题目还有实现限制一些细节,那更不用说了,也许是自己刚接触的缘故,不怎么熟悉,我想以后多做题应该就能够彻底掌握,刚学的知识都应该有个沉淀的过程。

并查集问题的示例代码:

find3(x)

{ r= x;while (set[r] <> r) //循环结束,则找到根节点

  r = set[r];       i = x;

  while (i <> r) //本循环修改查找路径中所有节点{  

      j = set[i];

      set[i] = r;

       i = j;}

}

Dijkstra算法——伪代码如下:

1. 初始化数组dist、path和s;

2.while (s中的元素个数<n)

     2.1 在dist[n]中求最小值,其下标为k;

     2.2 输出dist[j]和path[j];

     2.3 修改数组dist和path;

     2.4 将顶点vk添加到数组s中


0 0
原创粉丝点击