贪心算法:如何用贪心算法实现huffman压缩编码?

贪心算法:如何用贪心算法实现huffman压缩编码?

贪心算法(greedy algorithm) 有很多经典应用,Huffman coding、Prim和Kruskal最小生成树算法还有Dijkstra单源最短路径算法,如何利用贪心算法来实现对数据压缩编码有效节省数据存储空间?

如何理解“贪心算法”?

假设我们有一个可以容纳100kg物品的背包,有以下5种豆子,每种豆子的总量和总价值不同,为了让背包中所装物品的总价值最大,如何选择又装多少呢?

物品 总量(kg) 总价值(元)
黄豆 100 100
绿豆 30 90
红豆 60 120
黑豆 20 80
青豆 50 75

只有先算算每个物品的单价,按照单价从高到低排列依次来装即可,黑豆、绿豆、红豆、青豆、黄豆。所以是20kg黑豆,30kg绿豆、50kg红豆,这思路就是贪心算法

  • 当看到这类问题时候联想到贪心算法:针对一组数据,定义了限制值和期望值,希望从中选出几个数据,在满足限制值的情况下,期望值最大。限制值就是重量不超过100kg,期望就是总价值最大
  • 尝试是否可以用贪心算法解决:每次选择当前情况下,在对限制值同等贡献量的情况下,对期望值贡献最大的数据。刚才就是从剩下的豆子里面,选择单价最高的,即重量相同的情况下,对价值贡献最大的豆子
  • 举几个例子看贪心算法产生的结果是否是最优的。

在一个有权图中,从顶点s开始,找一条到顶点T的最短路径(边的权值和最小),贪心算法的解决思路是:每次都选择一条跟当前顶点相连的权最小的边,直到找到顶点。但是并不是最短路径,贪心算法不工作的原因是前面的选择会影响后面的选择,所以即便第一步选择最优的走法(边最短)可能结果无缘全局最优解

贪心算法实战分析

1.分糖果

有m个糖果和n个孩子,如果m<n,糖果只能分配给一部分孩子,每个糖果的大小不等,这m个糖果的大小分别是s1,s2,s3……sm,还有每个孩子对糖果大小的需求也是不一样的,只有糖果的大小大于孩子对糖果大小的需求的时候,孩子才能满足,假设n个孩子对糖果大小的需求分别是g1,g2……gn

如何分配糖果能尽可能满足最多数量的孩子?

抽象为从n个孩子中,抽取一部分孩子分配糖果让满足的孩子个数(期望值)最大,限制值就是糖果个数m

每次从剩下的孩子中找出对糖果大小需求最小的,然后发给他剩下糖果中能满足他的最小的糖果,这样满足的孩子个数最多的方案

2.钱币找零

假设我们有1¥,2¥,5¥,10¥,20¥,50¥,100¥,它们的张数分别是c1,c2,c5,c10,c20,c50,c100,现在要用这些钱来支付k¥,最少要用多少张纸币呢?

在贡献相同期望值(纸币数目)的情况下,希望多贡献点金额,可以让纸币数目最少

3.区间覆盖

有n个区间,区间的起始端点和结束端点分别是[l1,r1], [l2,r2] , [l3,r3],…… [ln,rn],从n个区间中选出一部分区间,这部分区间满足两两不相交(端点相交并不算相交),最多选出多少个区间?

区间:                     [6,8]   [2,4]  [3,5]  [1,5]   [5,9]  [8,10]
不相交区间:      [2,4]   [6,8]  [8,10]

解决问题的思路是:假设这n个区间中最左端点是 l min,最右端点是rmax,这个问题相当于选择几个不相交的区间,从左到右将[lmin,rmax]覆盖上,按照起始端点从小到大的顺序对这n个区间排序,每次选择的时候左端点跟前面的已经覆盖了的区间不重合的,右端点又尽量小的,这样可以让剩下的未覆盖区间尽可能的大可以放置更多的区间

如何用贪心算法实现Huffman编码?

有一个包含1000个字符的文件,每个字符占1字节(1byte = 8bits),存储这1000个字符一共就需要8000bits,有没有更加节省空间的存储方式呢?

这1000个字符中只包含6个不同字符,设它们分别是a,b,c,d,e,f而3个二进制位(bit)可以表示8个不同的字符,所以为了减少存储空间,每个字符我们用3个二进制位表示,那存储这1000个字符只需要3000bits即可

a(000) , b(001) , c(010) ,d(011) , e(100) , f(101)

霍夫曼编码不仅会考察文本中有多少个不同字符,还会考察每个字符出现的频率,根据频率的不同,选择不同长度的编码,如何给不同频率的字符选择不同长度的编码?把出现频率较高的字符用稍微短的编码,出现频率较少的字符用稍微长点的编码

解压缩的时候 ,每次从文本读取3位二进制码,然后翻译即可,但是霍夫曼编码是不等长的,每次应该取1位、2位、还是3位呢?为了避免歧义,霍夫曼编码要求每个字符的编码之间不会出现某个编码是另一个编码前缀的情况

011  010  100  011  101  001
 d       c         e      d        f      b

假设这6个字符出现的频率从高到低依次是a,b,c,d,e,f,我们把它们编码成下面这个样子,任何一个字符的编码都不是另一个的前缀,在解压缩的时候我们会读取尽可能长的可解压的二进制串,经过这种编码压缩之后,1000个字符只需要2100bits就可以了

字符 出现频率 编码 总二进制数
a 450 1 450
b 350 01 700
c 90 001 270
d 60 0001 240
e 30 00001 150
f 20 00000 100

如何根据字符出现频率的不同给不同的字符进行不同长度的编码呢?

把每个字符看做一个节点,并附带把频率放到优先级队列中,从队列中取出频率最小的两个节点A,B,然后新建一个节点c,把频率设置为两个节点频率之和,并把这个C节点作为A,B的父节点,把C放入到优先级队列中,重复直到队列中没有数据,最后形成一个总字符。给每条边加上画一个权值,指向左子节点的边统统标记为0,指向右子节点的边标记为1,那从根节点到叶节点的路径就是叶节点对应字符的霍夫曼编码

​ p

​ k a (1)

​ z b (01)

​ y c (001)

​ x d (0001)

f (00000) e(00001)

1.在一个非负整数a中,从中移除k个数字,让剩下的数字值最小,如何选择移除哪K个数字呢?

整数a,由若干位数字组成,从高位开始移除,移除高位数字比它低位数字大的那个:K次循环,意思就是由最高位开始,比较低一位的数字,如果高位大,移除,高位小,向右移动一位继续比较,循环K次

比如4556847594546 -> 第一次455647594546 ->第二次 45547594546 -> 第三次 4547594546 ->第四次447594546->第五次 44594546

2.有n个人等待被服务,但是窗口就一个,每个人需要被服务的时间长度不同,如何安排被服务的先后顺序,才能让n个人总的等待时间最短?

等待时间最短的开始服务

发布了75 篇原创文章 · 获赞 9 · 访问量 9184

猜你喜欢

转载自blog.csdn.net/ywangjiyl/article/details/104537624