贪心算法

贪心算法是一种一条路走到黑,不论如何也要达到目的的算法思想,有很多应用都是通过贪心算法来实现的,比如Huffman Coding,Prim,Kruskal和最小生成树算法。

首先,我们要如何才能理解贪心算法?

先看一个经典的背包问题。

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

在这里插入图片描述
既然都说了是总价值最大,那么就肯定需要算一下相对价值了,显而易见,黑豆的价值最高。

然后就让我们用贪心算法的思路来说明一个问题的解决步骤:

  1. 首先要联想到贪心算法:针对一组数据,我们定义了限制值和期望值,希望在满足限制值的情况下,达到最大期望。
  2. 思考是否可以使用贪心算法:计算出在限制值相同的情况下,能达到最大期望值的单位。
  3. 将思路带入题目,看看是否能达到最优:就像刚才的题目,看看会不会有比选用单位价值最高的豆子放进背包更好的策略。

以上是贪心算法的一般步骤,但是也并不是总能给出最优解。

举一个有向无环图的例子,求最短路径,从S开始到T,选出最短路径,贪心算法是每次都选取相邻边权值最小的边去组,显然是不行的,这就是特例,所以要考虑一下这种情况。

在这里插入图片描述

之所以这种情况下,贪心算法不起作用,是因为后面的选择会被前面影响,而不是之前的背包问题,相互不影响,换句话说,你走的每一步路都直接影响到了最终的总长度,于是也就无缘最优解了。

贪心算法实战

<1>分糖果

我们有m个糖果和n个孩子,现在要把糖果分给这些孩子吃,但是糖果少,孩子多(m<n),所以糖果只能分配给一部分的孩子。糖果的大小不同,所以每个孩子对于糖果的期望不同,当糖果大小满足孩子需求,才能得到满足。
糖果大小分别是s1,s2,s3…
孩子需求分为是g1,g2,g3…

问题:如何满足更多孩子?

解:按照上面说到的步骤,我们将问题按照步骤解决。

  1. 首先联想到贪心算法,贪心算法需要一个限制值和一个期望值: 这里,糖果的数量就是限制值,满足孩子的个数就是期望值。
  2. 思考是否可以使用贪心算法:既然我们的期望值是孩子的个数,那么肯定要尽量满足需求量低的孩子,因为就可以省下大糖果了。我们将孩子的期望值排序,然后每次都找出最低期望的孩子去满足,就可以完成贪心算法了。
  3. 思考是否有特例:每一个孩子都是独立的,如果我们按照顺序去安排满足孩子的顺序,就不会对后面产生影响,所以是没有特例的。
<2>钱币找零

假如我们有1 2 5 10 20 50 100元纸币,分别有c1 c2 c5 c10 c20 c50 c100张,我们要支付k元,最少要用多少张纸币。

解:还是按照刚刚的思路

  1. 首先联想到贪心算法,贪心算法需要一个限制值和一个期望值: 这里,100块钱就是限制值,期望值是最少用的纸币数量。
  2. 思考是否可以使用贪心算法:既然我们要的是最少纸币数量,那么只要先按照顺序把100块钱全部拿出去,再依次按照面额支付即可,然后到小的数额再去用1块钱或者两块钱纸币去补齐。
  3. 思考特例:其实这个确实是有特例的,但是还需要去验证。
<3>区间覆盖

假设有n个区间,区间的起始端点和结束端点为[l1,r1],[l2,r2],[l3,r3]…[ln,rn],我们要从这n个区间中选出一部分区间,这部分区间满足两两不相交,最多可以选出多少区间。

在这里插入图片描述

我们再次使用刚才的思路:

  1. 首先联想到贪心算法,贪心算法需要一个限制值和一个期望值:限制值就是区间的最左到最右,假设为[lmin,rmax],期望值就是选出的区间。
  2. 思考是否可以使用贪心算法:如果要完成这个题目,我们应该尽量让选出的区间覆盖程度更大化,然后保留那些左端点不会和前一个区间右端点不重合,右端点还尽量小的区间。
    在这里插入图片描述
<4>Huffman Coding的实现原理

首先来科普一下什么是哈夫曼编码

依据字符出现概率来构造异字头的平均长度最短的码字就叫做哈弗曼编码

假如有一个1000字符的文件,每个字符占用1 byte = 8 bits,如何更加节省空间,已知文件只有abcdef六种字符,而3个二进制位就可以表示8个不同的字符,那就是3000bits了,有没有什么更好的策略呢?

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

哈夫曼编码不仅仅会看文本中有多少字符,还会考察每个字符出现的频率,然后根据频率的不同,选择不同长度的编码,进一步增加压缩的效率。根据贪心算法,我们可以将频率高的字符,用短编码,对频率少的字符,用长编码。

我们刚才转化成的二进制编码,是等长表示字符,但是哈夫曼是不等长的,为了避免解压缩的歧义,所以哈夫曼编码要求不要让某个编码是另一个编码前缀

假设这六种字符的出现频率从大到小是abcdef ,于是我们可以这样设计:

在这里插入图片描述
重新计算,我们的占用字节数就又会少了很多。

但是哈夫曼树的难点不在于本身的思路,而是编码的方式。

下面来说一下编码方式:

我们把每一个字符看做一个节点,取出队列中频率最小的两个节点AB,新建一个节点C,然后让C作为父亲节点,AB作为子节点,然后把C放到优先队列,如图:

在这里插入图片描述

然后我们给边加上权值,左0右1

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值