算法设计技巧与分析 | 贪心算法

266 阅读4分钟

贪心算法

基本概念

  1. 是指算法每次做选择的时候是“贪心”的,也就是尽量选择从目前来看的最优解(局部最优解);如旅行商问题中,设目前处于城市ci,那么就选择一条从ci到其他未经过城市中最短的边。
  2. 局部最优选择,不一定能达到全局最优;对具有“最优子结构性质”的问题可以得到全局最优解,但对另外一些问题是无法获得全局最优解的。

钱币兑换

image.png

动态规划

step1:判断是否具有最优子结构性质:设X为面值为w的纸币的最优解,从X中却走一个面值为vi的硬币,即X.xi <- X.xi - 1,则新的解X'必然是面值为w-vi纸币的最优解,所以钱币兑换问题具有最优子结构性质。

step2:递归定义最优值:

image.png

step3:自底向上计算最优值

step4:根据最优值构造最优解

贪心算法

先兑换大面额的硬币,只有在大面额硬币无法兑换时,才兑换小面额的硬币。

image.png

贪心算法和动态规划

通过钱币兑换问题,我们发现贪心算法和动态规划对子问题的定义是不一致的。动态规划的子问题有n个(即y-vi);而贪心算法子问题就非常简单,y=y-vi,vi是贪心选择得出的唯一解。

如果设X^n为原问题的贪心解,x为原问题的当前贪心选择,X^n-1为子问题的贪心解,则有以下等式:

image.png

贪心解是最优解吗?

替换法

用贪心算法得出的解(x1,x2,...,xn)和最优解(y1,y2,...,yn)中的元素依次进行比较,如果元素xi和yi相同,则将最优解中的yi替换成xi,显然替换前后解的形式不变,则仍为最优解;如果不同,还是要将yi替换成xi,但此时,还需证明替换后的解依然是最优解。

归纳法

  • 贪心选择是正确的:x=x*
  • 问题具有最优子结构性质:Xn = x ∪ X*n-1

小数背包问题

给定n种物品和一背包,物品i的重量是wi,其价值为vi,背包的承重为C。要求把物品装满背包,且使背包内的物品价值最大,在选择某物品时,可以只选择物品的一部分装入背包。

image.png

image.png

image.png

image.png

image.png

贪心算法不一定能够得出0-1背包的最优解

最小生成树

image.png

image.png

Kruskal算法

  1. 初始化:将图G=(V,E)初始化为n个独立的顶点,并将所有的边按权值从小到大排序;
  2. 依次遍历排好的边,如果边eij的两个顶点vi和vj属于不同的连通分支,则将此边加入到树中,否则忽略此边。

image.png

image.png

对边排序(语句7)复杂度为O(mlogm),第二个for循环(语句8-13)执行m次,循环体内的FIND语句(语句9)的复杂度为O(logn),即第二个for循环的复杂度为O(mlogn),所以算法的总复杂度为O(mlogm+mlogn) = O(mlogm)

image.png

image.png

Prim算法

  1. 初始化:所有节点的weight值为∞,prev值设为null,选择任意节点作为根节点,根节点的weight置为0,prev值为自身,并将根节点放入T中(最终的T为最小生成树),并将根节点设为当前节点;
  2. 将所有和当前节点直接相连的节点weight进行更新,即如果和当前节点相连的边的权重小于节点的weight值,则更新,且将prev值设为当前节点,否则,不进行任何更新;
  3. 在剩余的节点中,选择一个weight值最下的节点加入到T中,并将此节点作为当前节点;
  4. 重复步骤2和3,直到所有的节点加入到T中。

image.png

image.png

霍夫曼编码

  • 数据压缩:应用网络、磁盘数据压缩,减少数据的传输量和存储量;
  • 霍夫曼编码广泛的应用在数据压缩,可节省约20%-90%的数据量。

image.png

image.png

image.png

image.png

image.png

算法流程

假设F是一个n个字符的集合,每个字符c的频率由函数c.freq给出。

  1. 将所有字符按频率形成最小堆T;
  2. 从T中依次提取堆的根节点x和y,并将其合并为z,z的频率为x和y频率之和,其两个子节点分别为x和y,之后,将z插入到堆T;
  3. 重复以上步骤,堆中只有一个元素为止。

image.png

image.png

image.png

**算法复杂度为O(nlogn)

image.png

image.png

image.png