算法考试复习之胡思乱记-----贪心法

贪心法

贪心法顾名思义就是说要贪,要一点一点的贪,歇斯底里地贪,嚼字一点的讲,就是说求一个问题的最优解时,将这个问题肢解为一系列的局部性的问题,然后通过在每个局部得到最优以使得在全局得到最优.其实这点挺有意思的,整个计算机界每天都在叫着虚拟现实,为此做了很多的算法,大部分算法都比较贪,结果现实给我们虚拟出来了.当然,即使在现实世界里面贪也就不见得全是坏事,所以这么想来也只能说是现实世界是比较残酷的吧.可以想象,差不多所有写程序不贪的程序员都下岗或面临下岗了吧.

当然对于局部最优到全局最优这个过程是不是可行的,不同的解决方法自然有不一样的答案了,如果你的方法是对的,归纳法会告诉你的,如果你的方法是错的,归纳法也会告诉你的.

经典问题之一是图的最小生成树的算法.生成树的意思就是说包含图中所有顶点的连通无环的子图,而最小生成树是指在生成树里面树的权重最小的那一个棵。主要的方法有Prim算法和Kruskal算法.

1. Prim算法
它是Bell的R. C. Prim在57年提出的算法,不过他也并不全是这个算法最最祖先的人,最早的应该是捷克人V. Jarník在1930年提出的算法,不过他的文章是用捷克语写的,光看标题就看不明白了。有人也把这个算法就叫做Prim-Jarnik算法。Prim算法简单有效,他从图的一个指定的顶点出发,用V表示树顶点集,初始只有那个指定的顶点;E表示最终最小生成树中有的边的集合,初始是空集。从V中的顶点出发,找到从这个顶点出发的权值最小的边,如果这条边不构成回边,那么选入最小生成树,并将这条边加到E集合中,边的另一端结点加入到V集合中。这样一直循环到V中的顶点为图的所有顶点为止,最小生成树就存在E集合里面了。

证明这个算法是否正确还是利用归纳法,在从T(i-1)是最小生成树到T(i)是最小生成树的判定过程中使用反证法,就可以证明这样选出来的树一定是最小生成树。

显然这个算法还有它的提升空间,但是不管怎么提升,基础性的想法不会有什么变化,所以不会有什么质的差别,不值得再提了。这就是基础科学的魅力,它是57年的算法,人们到今天还会津津乐道。

2. Kruskal算法
这个算法是Kruskal J.B在1956年的论文里面提出来的,又是一个爷爷级的算法。它从边的角度出发,每一次将图中的权值最小的边取出来,在不是回边,也就是说不构成环的情况下,将边加入最小生成树中,重复这个过程,直到所有的图中所有的点都加入到最小生成树中结束。

3. 两种算法的比较
从空间上讲,显然在Prim算法中,我们只需要很小的空间就可以完成算法,因为每一次我们都是从个别点开始出发进行扫描的,而且每一次扫描也只扫描与当前顶点集对应的边,但在Kruskal算法中,因为我们每时每刻都得知道当前的树里面权值最小的边在哪里,这样我们需要对所有的边进行排序。对于很大的图来讲,这个需要占用比Prim算法大的多的空间。从时间上讲,Prim算法如是果用Linked List表示图,用最小堆实现优先级对列的话,算法的复杂度为O(|E|log|V|),而在Kruskal算法中,如果排序算法很高效的话,Kruskal算法的时间复杂度为O(|E|log|E|),这样比起来,好像Prim算法的确比Kruskal算法更好一些。

第二个经典的问题也是跟图有关,Dijkstra算法

大家都了解Dijkstra是ACM图灵奖的获得者,荷兰人。他在算法,操作系统,分布式处理等许多方法都有极高深的造诣,他于1999年从大学中正式退休,在2002年8月去世。他的一生绝对可以算得上是那种直接叫Amazing Life的那种的了,是一个计算机科学家可以达到的顶点位置。这个算法是在59年发表的,当时的Dijkstra29岁,从阿姆斯特丹大学博士毕业,当了程序员6年程序员之后,天才加经验了,完全的无敌模式。这个算法发表3年之后,在1962年他就回到大学开始当教授,并开始一次次创造奇迹,创造历史。

Dijkstra算法研究的问题是单起点最短路径的问题。它的输入就是一个连通图,再加上一个顶点s。输出对于图中所有顶点来说从起始顶点s到它们的最短路径长度和路径上的倒数第二个顶点。

算法的细节问题没什么必要细说,这里有一个Java Applet的算法的演示http://www-b2.is.tokushima-u.ac.jp/~ikeda/suuri/dijkstra/Dijkstra.shtml,一定比说话说得清楚。


在用邻接链表表示图,最小堆表示优先队列的情况下,Dijkstra算法的复杂度为O(|E|log|V|)。

第三个经典问题是背包问题。就是n件物品一个可以容纳重M的物品的包,每件物品的重量不一样,且他们的价值也都不一样,用怎么样的方式来把物品放在包里面,以使包中的物品的价值最大。如果每件物品可以分成几份的话,就是广义的背包问题,如果一件物品要么拿要不就不拿,这个问题归为0/1背包问题。

这个问题其实最好地展示了贪心这个概念,涉及到价值了,而且有一种想抢又怕手短的急迫感。解决方法当然其实相当简单的,要不你用物品的价值排序,用价值当标准来选,直到选到不能再装为止。要么用价值除去重量排序,按这个排序的结果顺序能装多少装多少,就是一个价重比高者优先的想法。也就是说在贪心的世界里面,走出的每一步都像是老妈在买菜一样,稳,准,狠,不到万不得已,决不放手。不过可以证明的是,当用价重比进行排序装包的时候,得到的结果是最优的。证法当然还是归纳法。

更有趣的是,如果要装的物品是人的话,我又是物品中的一个,不管用哪种排序,我都不会被选中而放到包里,这再一次证明世界是残酷的,尤其对于体重超过一定数量的人来说。。。。。。

第四个问题为处理器调度问题,假定有n个处理机,m个任务,每个作业占用处理机的时间一定,如何将作用分配给各个处理机。第一种方法就是按作业进行操作的顺序直接分,当前最闲的处理机执行下一个任务。直到分完所有的任务。这个方法当然不是最优的,像是早期纸带机的那种顺序操作的感觉。第二种方法就是将作业要用的时间排序,然后将作业分给当前最空闲的处理机。这种方法听起来有点像多进程调度的方法。事实上这两种方法都不是最优的,在动态规划和分支限界法里面有更优的方法可用.


贪心法大概就这么多了,当然涉及到算法细节和实现的时候会遇到更多的问题,不再继续下去了,必竟是个考试,抓面不抓点的做法是比较“贪心”的选择,收获会大一点,回头有时间好好往细搞。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值