秒懂算法 | 哈夫曼编码贪心算法

简介: 讲解哈夫曼编码算法的贪心策略及正确性证明。

640.jpg


哈夫曼编码是一种变长码编码方式,该编码方式是数学家D.A.Huffman于1952年提出,其完全依据字符出现频率来构造平均长度最短的码字。简言之,哈夫曼编码算法是用字符出现的频率来建立一个用0-1串表示各字符的最优表示方式,有时称之为最佳编码,一般就叫作Huffman编码。

01、问题分析——贪心策略

首先,仔细研究编码方案的二叉树结构,不难发现以下4点。

(1) 树的叶子节点为字符。

(2) 从根到叶子的路径经过的01串为相应字符的编码。

(3) 编码长度为该字符在二叉树中的深度。

(4) 编码方案满足前缀码性质。

其次,研究编码方案优劣的衡量标准。平均每个字符的码长,引入平均码长的概念。所谓平均码长指的是编码方案中平均每个字符的码长。

设字符集C,任意一个字符c∈C,出现的频率为f(c),在二叉树T中的深度为dT(c)(即字符编码的长度),二叉树T表示的编码方案平均码长为B(T),则:

640.png


哈夫曼编码是使平均码长为最短的编码方式。哈夫曼以字符的使用频率做权构建一棵哈夫曼树,然后利用哈夫曼树对字符进行编码。核心思想是:频率越大的字符离树根越近。

算法的贪心策略是:从树的集合中选取两个频率最低的字符,使其作为左右子树构造一棵新树,父节点的频率为左右节点频率之和,然后将新树插入到树的集合中。

02、算法设计

1●设计思想

输入:字符集C={c1,c2,…,cn}及字符出现的频率f(ci),i=1,2,…,n。

输出:哈夫曼树Q。

首先将字符集中的每个字符看作一棵只含有根节点的树,构造一个n棵树构成的树的集合Q;然后做n-1次贪心选择,每次都选择两个出现频率最小的节点,让其作为左右子树构造一棵新树,将新树插入到树的集合Q中。直到Q中只含有一棵树为止。
从树根深度优先遍历,左子树输出0、右子树输出1,搜索到叶子节点就得到了叶子字符的编码。

2●算法伪码

算法:Huffman(C)

640.png

3●正确性证明

设C是字符集,任意一个字符c的频率为f(c);x、y是C中具有最小频率的两个字符。

(1) 贪心选择性质——存在从贪心选择开始的最优解。

需证明存在字符集C的一个最优前缀码方案T,使得x、y具有相同的码长,且最后一位编码不同。

如果T中,x、y是最深的叶子且互为兄弟,那么T就是贪心选择开始的最优前缀码;

如果T中,x、y不是最深的叶子,也不是兄弟,那么设T中字符b、c是最深的叶子且互为兄弟,如图2-7所示。

由于x,y是字符集C中频率最小的两个字符,所以有f(x)≤f(b)、f(x)≤f(c)、f(y)≤f(b)、f(y)≤f(c),交换树T中的字符x和字符b得到树T′,如图2-8所示。

640.png


■ 图2-7树T结构示意图 ■ 2-8树T′结构示意


在树T和T′中,只有x、b两个字符深度发生变化,其他字符深度都没有变,所以:

640.png

又由于dT(x)=dT′(b),dT(b)=dT′(x),所以:

640.png

又f(x)-f(b)≤0,dT(x)-dT(b)≤0,

故B(T)-B(T′)≥0,B(T)≥B(T′)。

再交换字符y和字符a,得到树T″,如图2-9所示。

640.png


■ 图2-9 树T″结构示意


同理可以证明B(T′)≥B(T″),由此B(T)≥B(T″)。

又因为T是字符集C的最优前缀码,所以B(T)≤B(T″)。所以B(T)=B(T″),T″中,x、y字符处于最深处且互为兄弟,是从贪心选择开始的最优解。

(2) 最优子结构性质——整体最优解一定包含子问题的最优解。

设T是字符集C的最优前缀码,令f(z)=f(x)+f(y),则T′是字符集C′=C-{x,y}+{z}的最优前缀码。

需要证明T′是字符集C′=C-{x,y}+{z}的最优前缀码。

证明: 假设T′不是字符集C′的最优前缀码,则设T″是字符集C′的最优前缀码,B(T′)>B(T″)。
将字符x、y加入到T″中,作为字符z的孩子,构成的树为T"',则有T"'是字符集C的一种编码方案。

对任意字符c∈C-{x,y},有dT(c)=dT′(c),故f(c)dT(c)=f(c)dT′(c),另一方面dT(x)=dT(y)=dT(z)+1。


640.png


由此,可以知道,B(T)=B(T′)+f(x)+f(y),同理有B(T'″)=B(T″)+f(x)+f(y)。

由于B(T′)>B(T″),所以B(T)>B(T'″)。

这说明T不是字符集C的最优前缀码,这与T是字符集C的最优前缀码矛盾,假设不真,得证。

目录
相关文章
|
11天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
28 2
|
1月前
|
算法 Java C++
【贪心算法】算法训练 ALGO-1003 礼物(C/C++)
【贪心算法】算法训练 ALGO-1003 礼物(C/C++)
【贪心算法】算法训练 ALGO-1003 礼物(C/C++)
|
2月前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
3月前
|
算法 5G vr&ar
基于1bitDAC的MU-MIMO的非线性预编码算法matlab性能仿真
在现代无线通信中,1-bit DAC的非线性预编码技术应用于MU-MIMO系统,旨在降低成本与能耗。本文采用MATLAB 2022a版本,深入探讨此技术,并通过算法运行效果图展示性能。核心代码支持中文注释与操作指导。理论部分包括信号量化、符号最大化准则,并对比ZF、WF、MRT及ADMM等算法,揭示了在1-bit量化条件下如何优化预编码以提升系统性能。
|
3月前
|
算法
【Azure Developer】完成算法第4版书中,第一节基础编码中的数组函数 histogrm()
【Azure Developer】完成算法第4版书中,第一节基础编码中的数组函数 histogrm()
|
4月前
|
机器学习/深度学习 存储 算法
编码之舞:从算法到应用的探索之旅
在数字化时代的浪潮中,编程技术如同一种语言,连接着人类与机器。本文将带领读者踏上一场自数据结构基础至高级算法应用的探索旅程,通过实际案例分析,揭示算法在现代软件开发中的重要作用,并分享作者在编程实践中的心得体会,旨在为初学者和资深开发者提供有价值的参考与启示。
|
3月前
|
算法
【算法】贪心算法——柠檬水找零
【算法】贪心算法——柠檬水找零
|
3月前
|
算法
【算法】贪心算法简介
【算法】贪心算法简介
|
4月前
|
机器学习/深度学习 算法 计算机视觉
通过MATLAB分别对比二进制编码遗传优化算法和实数编码遗传优化算法
摘要: 使用MATLAB2022a对比了二进制编码与实数编码的遗传优化算法,关注最优适应度、平均适应度及运算效率。二进制编码适用于离散问题,解表示为二进制串;实数编码适用于连续问题,直接搜索连续空间。两种编码在初始化、适应度评估、选择、交叉和变异步骤类似,但实数编码可能需更复杂策略避免局部最优。选择编码方式取决于问题特性。