37|贪心算法:如何用贪心算法实现Huffman压缩编码?
贪心、分治、回溯、动态规划这4个算法思想,原理解释起来都很简单,但是要真正掌握且灵活应 用,并不是件容易的事情
贪心算法有很多经典的应用,比如霍夫曼 编码(Huffman Coding)、Prim和Kruskal最小生成树算法、还有Dijkstra单源最短路径算法。最小 生成树算法和最短路径算法我们后面会讲到,所以我们今天讲下霍夫曼编码,看看它是如何利用贪 心算法来实现对数据压缩编码,有效节省数据存储空间的。
如何理解“贪心算法”?
我总结一下贪心算 法解决问题的步骤,我们一起来看看。 第一步,当我们看到这类问题的时候,首先要联想到贪心算法:针对一组数据,我们定义了限制值 和期望值,希望从中选出几个数据,在满足限制值的情况下,期望值最大。 类比到刚刚的例子,限制值就是重量不能超过100kg,期望值就是物品的总价值。这组数据就是5种 豆子。我们从中选出一部分,满足重量不超过100kg,并且总价值最大。 第二步,我们尝试看下这个问题是否可以用贪心算法解决:每次选择当前情况下,在对限制值同等 贡献量的情况下,对期望值贡献最大的数据。 类比到刚刚的例子,我们每次都从剩下的豆子里面,选择单价最高的,也就是重量相同的情况下, 对价值贡献最大的豆子。 第三步,我们举几个例子看下贪心算法产生的结果是否是最优的。大部分情况下,举几个例子验证 一下就可以了。严格地证明贪心算法的正确性,是非常复杂的,需要涉及比较多的数学推理。而 且,从实践的⻆度来说,大部分能用贪心算法解决的问题,贪心算法的正确性都是显而易⻅的,也 不需要严格的数学推导证明。
实际上,用贪心算法解决问题的思路,并不总能给出最优解。
3.区间覆盖
假设我们有n个区间,区间的起始端点和结束端点分别是[l1, r1],[l2, r2],[l3, r3],......,[ln, rn]。 我们从这n个区间中选出一部分区间,这部分区间满足两两不相交(端点相交的情况不算相交), 最多能选出多少个区间呢?
![](http://upload-images.jianshu.io/upload_images/814874-e16fef2e7f03e2f0.png)
这个问题的处理思路稍微不是那么好懂,不过,我建议你最好能弄懂,因为这个处理思想在很多贪心算法问题中都有用到,比如任务调度、教师排课等等问题。
这个问题的解决思路是这样的:我们假设这 n 个区间中最左端点是 lmin,最右端点是 rmax。这个问题就相当于,我们选择几个不相交的区间,从左到右将 [lmin, rmax] 覆盖上。我们按照起始端点从小到大的顺序对这 n 个区间排序。
我们每次选择的时候,左端点跟前面的已经覆盖的区间不重合的,右端点又尽量小的,这样可以让剩下的未覆盖区间尽可能的大,就可以放置更多的区间。这实际上就是一种贪心的选择方法。
![](http://upload-images.jianshu.io/upload_images/814874-a2e4a8707b4d2dff.png)
贪心算法:N位数删除K个数字,使剩下的数字串最小
题目:一个n位的数,去掉其中的k位,问怎样去掉使得留下来的那个(n-k)位的数最小?
分析:(删数问题,可用贪心算法求解),方法就是从简单入手,慢慢复杂。从n=1开始推导就会发现规律,
现在假设有一个数,124682385,
假如k = 1,则结果为12462385,k = 2,结果为1242385……
可以知道:最优解是删除出现的第一个左边>右边的数,因为删除之后高位减小,很容易想...那全局最优解也就是这个了,因为删除S个数字就相当于执行了S次删除一个数,因为留下的数总是当前最优解...
def solution(num, k):
s = str(num)
flag = True
while k:
for i in range(len(s)-1):
#每次删除第一个比下一个数字大的数
if s[i] > s[i+1]:
s = s.replace(s[i],'',1)
flag = False
break
#如果所有数字递增,则删除最后几个数字直接返回
if flag:
s = s[:len(s)-k]
k -= 1
return int(s)
- 假设有 n 个人等待被服务,但是服务窗口只有一个,每个人需要被服务的时间长度是不同的,如何安排被服务的先后顺序,才能让这 n 个人总的等待时间最短?
一直时间最短的先服务(堆)