算法课笔记系列(五)—— 图(Part1)

来源:互联网 发布:扑克王软件下载 编辑:程序博客网 时间:2024/05/21 17:41

半期后开始的第一个算法是图。这部分内容蛮多的,老师也讲的很快。所以写作业之前还是先梳理一下。这部分会分为两次课,这是第一部分。

首先是图里最简单和经典的深度优先搜索(Depth-FirstSearch)和广度优先搜索(Breadth-First Search)。

先需要了解一个对图的遍历。输入一个图G=(V, E), v ∈V,如果对从v出发的所有结点u都可达,那么VISITED(u)就设为true。

        

其中,PREVISIT 和 POSTVISIT算法是可选。当一个顶点是第一次被遍历或者是最后一次被剩下,这两个方法才会被调用。

可以证明,EXPLORE(G, v)是正确的,也就是说,可以访问到所有从v可达的结点。这里简单证明一下,从两个方面。首先它访问的每个结点都一定是可以从v可达的,因为该算法只从结点移动到它们的邻居结点,因此不能够跳到v不可达的区域;其次,每一个从v可达的结点最终都会被访问到,我们假设有某个结点u被该算法忽略了,选择任何从v到u的路径,观察这个过程中在该路径上实际最后访问的结点z。令w为相同路径之后下一个要访问的结点,因此z会被访问,但是w不能。这就产生一个矛盾,如果该算法能够访问到z,那么肯定就可以观察到w,并移动到w的位置访问。


DFS的算法过程如下:

        

一个例子,假设我们使用字母表的顺序来遍历G:

        

运行循序为下面的数字标识。

因为VISITED是一个数组,因此,每一个结点都只会被EXPLORE(G, v)执行一次。在整个过程中,有以下两个步骤:

1) 一些固定工作,比如对结点做标记,以及PRE/POSTVISIT。整个过程需要O(|V|)。

2) 相邻边扫描的一个循环,对于每一个结点,循环的时间成本不同。在DFS中,每一条边<x, y>都会被检查两次,分别在EXPLORE(G, x)和EXPLORE(G, y)。因此总共时间是O(|E|)。

因此,深度优先搜索运行时间为O(|V| + |E|).

    如果在任意结点对之间都有一条路径,那么,偶们称这个无向图是连接的。连通组件是内部连接的一个子图,但是没有与剩下结点相连接的边。DFS的外循环每一次调用EXPLORE,一个新的连通组件就会被选择。DFS可以轻易地检查一个图是否是连通的。对于任意的结点u和v,两个区间[PRE(u), POST(u)]和[PRE(u), POST(u)]要么是不相交的,要么其中一个是另一个的子集。

DFS生成一个搜索树或者森林,有根,有祖先和后代,有双亲和孩子。有一种边的类型,树边事实上是DFS森林的一部分,前向边在DFS树中从一个结点指向一个非孩子的后代,后向边在DFS树中指向一个祖先,交叉边既不是指向祖先也不是指向后代,它们指向已经被完全遍历过的结点。

有向无环图(DAG,directed acyclic graphs):

有向图中的环是一个循环路径:


 一个有向图有一个环当且仅当它的深度优先搜索显示了一个后向边。这里就不做证明了.

一个有向图的连通性在于,两个结点u和v是连通的,如果它们之间存在一条路径,该路径可以是从u到v的,或者是从v到u的。这样的关系将V分离成不连通的集合,称为强连通组件。每一个有向图都是它的强连通组件的有向无环图。


BFS的算法步骤如下:

         

一个例子,假设我们使用字母表的顺序来遍历G:

        

对于每一个d=0,1,2,…,会存在这么一个时刻,从s出发距离小于等于d的所有结点都有使得它们的距离被正确设置,所有其他的结点的距离都被设为∞,以及队列中只包含距离为d的结点。BFS的时间运行成本也是O(|V| + |E|)。


接着是最小生成树(Minimum Spanning Tree,MST)。

这一部分是之前在贪心算法中的剩余一部分没讲的内容。最小生成树有许多的应用,如网络设计,NP难问题的近似算法,聚类分析,最大瓶颈路径等直接应用等。

给定一个连通图G=(V,E),每一条边的权值为Ce,一颗最小生成树是有着边E的子集T使得T是一颗边的权重和最小的生成树(包含所有顶点)。

如下图:

                 

使用贪心算法来生成一棵MST。

第一种算法是Kruskal算法。首先令T为空集,以边权重递增顺序开始考虑,不断往T中插入边e,除非会生成一个环。

        

排序需要O(m log n),联合查询需要O(m α (m, n))

第二种算法是反删除算法。也就是令T=E,以边权重递减顺序考虑,不断将e从T中删除,除非会导致T不相连通。

第三种算法是Prim算法。从一些根节点s开始,贪心的从s向外扩展生成一棵树T。在每一个步,将权重最小的边e加到只有一个端点的T中。

                

Prim算法的复杂度为,使用数组的话O(n2),使用二元堆的话为O(m log n)

考虑一个简化情况是,所有边的权重都是不同的。

切割性质:令S是结点的任何一个子集,e是与S中的恰恰一个端点联系的一条有最小权重的边,那么MST包含e。

环性质:令C是任意的环,f是属于C的有着最大权重的边,那么MST不包含f。

证明这里就省略。

也可以使用这两种性质的交来选择。

聚类分析是最小生成树的一个应用。给定一个标记着p1,p2,…pn的n个对象(照片啊,文档啊,微生物等等)的集合U,将其分类成关联的多个群组。K聚类则是将对象分成k个非空的组。间隔是指在不同的聚类中,任何对点之间的最小距离。所以,最大间隔聚类是给定一个整数k,找到一个有着最大间隔的k聚类。

 

本节课的最后讲了一下最短路径问题。

从u到v的最短的路径是指从u到v有着最小权重的路径,表示为=min{w(p):p是从u到v的路径},其中

                     

=∞表示从u到v之间不存在路径。

一条最短路径的子路径也是最短路径。三角不等式原理:

                   

对于所有的u,v,x ∈V,

第一个算法是Dijkstra's algorithm.

         

简单来说,Dijkstra算法是用来找a和b之间的最短路径。首先选择一个有着最短距离的未访问过的结点开始,计算与它所有相邻的未访问过的邻居结点之间的距离,如果更小,则更新邻居结点距离。访问过后标记已访问的结点。

其时间复杂度为:

        

0 0