贪心算法
基本概念
- 是指算法每次做选择的时候是“贪心”的,也就是尽量选择从目前来看的最优解(局部最优解);如旅行商问题中,设目前处于城市ci,那么就选择一条从ci到其他未经过城市中最短的边。
- 局部最优选择,不一定能达到全局最优;对具有“最优子结构性质”的问题可以得到全局最优解,但对另外一些问题是无法获得全局最优解的。
钱币兑换
动态规划
step1:判断是否具有最优子结构性质:设X为面值为w的纸币的最优解,从X中却走一个面值为vi的硬币,即X.xi <- X.xi - 1,则新的解X'必然是面值为w-vi纸币的最优解,所以钱币兑换问题具有最优子结构性质。
step2:递归定义最优值:
step3:自底向上计算最优值
step4:根据最优值构造最优解
贪心算法
先兑换大面额的硬币,只有在大面额硬币无法兑换时,才兑换小面额的硬币。
贪心算法和动态规划
通过钱币兑换问题,我们发现贪心算法和动态规划对子问题的定义是不一致的。动态规划的子问题有n个(即y-vi);而贪心算法子问题就非常简单,y=y-vi,vi是贪心选择得出的唯一解。
如果设X^n为原问题的贪心解,x为原问题的当前贪心选择,X^n-1为子问题的贪心解,则有以下等式:
贪心解是最优解吗?
替换法
用贪心算法得出的解(x1,x2,...,xn)和最优解(y1,y2,...,yn)中的元素依次进行比较,如果元素xi和yi相同,则将最优解中的yi替换成xi,显然替换前后解的形式不变,则仍为最优解;如果不同,还是要将yi替换成xi,但此时,还需证明替换后的解依然是最优解。
归纳法
- 贪心选择是正确的:x=x*
- 问题具有最优子结构性质:Xn = x ∪ X*n-1
小数背包问题
给定n种物品和一背包,物品i的重量是wi,其价值为vi,背包的承重为C。要求把物品装满背包,且使背包内的物品价值最大,在选择某物品时,可以只选择物品的一部分装入背包。
贪心算法不一定能够得出0-1背包的最优解
最小生成树
Kruskal算法
- 初始化:将图G=(V,E)初始化为n个独立的顶点,并将所有的边按权值从小到大排序;
- 依次遍历排好的边,如果边eij的两个顶点vi和vj属于不同的连通分支,则将此边加入到树中,否则忽略此边。
对边排序(语句7)复杂度为O(mlogm),第二个for循环(语句8-13)执行m次,循环体内的FIND语句(语句9)的复杂度为O(logn),即第二个for循环的复杂度为O(mlogn),所以算法的总复杂度为O(mlogm+mlogn) = O(mlogm)
Prim算法
- 初始化:所有节点的weight值为∞,prev值设为null,选择任意节点作为根节点,根节点的weight置为0,prev值为自身,并将根节点放入T中(最终的T为最小生成树),并将根节点设为当前节点;
- 将所有和当前节点直接相连的节点weight进行更新,即如果和当前节点相连的边的权重小于节点的weight值,则更新,且将prev值设为当前节点,否则,不进行任何更新;
- 在剩余的节点中,选择一个weight值最下的节点加入到T中,并将此节点作为当前节点;
- 重复步骤2和3,直到所有的节点加入到T中。
霍夫曼编码
- 数据压缩:应用网络、磁盘数据压缩,减少数据的传输量和存储量;
- 霍夫曼编码广泛的应用在数据压缩,可节省约20%-90%的数据量。
算法流程
假设F是一个n个字符的集合,每个字符c的频率由函数c.freq给出。
- 将所有字符按频率形成最小堆T;
- 从T中依次提取堆的根节点x和y,并将其合并为z,z的频率为x和y频率之和,其两个子节点分别为x和y,之后,将z插入到堆T;
- 重复以上步骤,堆中只有一个元素为止。
**算法复杂度为O(nlogn)