您的位置:首页 > 其它

MIT算法导论-第12讲-最小生成树-Prim算法

2016-01-15 22:02 120 查看

问题定义

输入:无向图G=(V,E),每条边有一个权重,另假设所有权值是不同的输出:一棵生成树,连接了所有顶点,权重总和最小。

分析过程

(http://www.cnblogs.com/numbersix/p/4909750.html)

1. MST拥有最优子结构:去掉MST的一条边,MST分裂成两个子树T1、T2,则T1、T2分别是子图G1、G2的MST。

1.1 子图G1只包含T1的顶点, 子图G2只包含T2的顶点,假设拿掉的边为(u,v),u在G1内,v在G2内
1.2 如果G1的MST不是T1而是T1',根据MST的性质(连接了所有顶点),那么u也在T1'内,此时将T1'与T2通过(u,v)相连,可以得到一个新的子树T', T'权值小于T,与T是MST的前提矛盾。


2. MST拥有重叠子问题:拿掉x条边使得T分为多个子树,即使这x条边拿掉的顺序不同,得到的子树集是一样的

3. MST的贪心选择属性(Greedy choice property):局部最优解也是全局最优解。将G分为两个子图,设(u,v)是连接该两个子图的边之中权值最小的边(局部最优解),则(u,v)也是最小生成树里的一条边(属于全局最优解)

3.1用剪贴法证明:连接两个子图的边中,如果有其他的边(u',v')小于(u,v),则可以裁剪掉(u,v),用(u',v')连接两个子图,得到一个总权值更小的MST,与前提矛盾


Prim算法

基本思想:

1. 在图G=(V, E) (V表示顶点 ,E表示边)中,从集合V中任取一个顶点(例如取顶点v0)放入集合 U中,这时 U={v0},集合T(E)为空。

2. 从v0出发寻找与U中顶点相邻(另一顶点在V中)权值最小的边的另一顶点v1,并使v1加入U。即U={v0,v1 },同时将该边加入集合T(E)中。

3. 重复2,直到U=V为止。 这时T(E)中有n-1条边,T = (U, T(E))就是一棵最小生成树。

朴素的Prim算法如果使用邻接矩阵来保存图的话,时间复杂度是O(N^2),N+2N+…+N*(N-1),可见时间主要浪费在每次都要遍历所有点找一个最小距离的顶点,可以使用最小优先队列优化。

Prim算法的堆优化:

为了有效的实现Prim算法,需要一种快速的方法来选择一条新的边。在下面的伪代码中,连通图G和最小生成树的根节点r作为算法的输入,图采用邻接链表的形式存储。在算法的执行过程中,所有不在树A中的结点都存放在一个基于key属性的最小优先队列Q中。对于每个结点v,属性v.key保存的是连接v和树中其他结点的边中最小边的权重。约定:如果不存在这样的边,那么v.key=∞。属性v.π给出的是结点在树中的父节点。当Prim算法终止时,最小优先队列为空。



时间复杂度分析:

1.如果Q实现为一个二叉最小优先队列,我们可以使用Build-Min-Heap来执行算法的第1-5行,时间成本为O(V)。while循环中的语句一共要执行V次,EXTRACT-MIN操作的总时间为O(VlgV)。由于所有邻接链表的长度之和为2E,算法第8-11行的for循环的总执行次数为O(E)。算法第11行的赋值操作涉及一个隐含的decrease-key操作,该操作在二叉树最小堆上执行时间成本为O(lgV)。因此,Prim算法的总时间代价为O(VlgV+ElgV)。

2.如果使用fibonacci堆来实现最小优先队列Q。Prim算法的渐进运行时间可以得到进一步改善。如果fibonacci堆中有V个元素,则extract-min操作的时间摊还代价为O(lgV),二decrease-key操作的摊还时间代价为O(1),因此,如果使用fibonacci堆来实现最小优先队列Q,则Prim算法的运行时间将改进到O(E+VlgV)。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: