编码理论课程论文

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

解析哈夫曼编码的基本应用

【摘要】哈夫曼编码(Huffman Coding)是一种编码方式,它以哈夫曼树(最优二叉树)、带权路径长度最小的二叉树为基础,并且经常应用于数据压缩和解压缩。在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称"熵编码法"),用于数据的无损耗压缩。利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。

【关键词】哈夫曼树压缩解压缩

前言

在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUFFMAN)树和哈夫曼编码。哈夫曼编码是哈夫曼树的一个应用。本文对哈夫曼编码的应用进行了简单的介绍,系统地概括了哈夫曼树的建立、压缩和解压缩的过程。使读者更易理解哈夫曼编码的应用,可以大大提高通信的信道利用率,缩短信息传输时间,降低传输成本。

一、哈夫曼编码的基本简介

(一)哈夫曼编码的基本介绍

哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码

(VLC)的一种。 Huffman于1952年提出一种编码方法,该方法完全依据字符出现

概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫作

Huffman编码。以哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常

应用于数据压缩。在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又

称"熵编码法"),用于数据的无损耗压缩。这一术语是指使用一张特殊的编码表

将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,

它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较

短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平

均期望长度降低,从而达到无损压缩数据的目的)。这种方法是由David.A.Huffman

发展起来的。

(二)哈夫曼编码的背景

哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。哈夫曼压

缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用

一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序

列,而那些很少出现的符号,则用较长的位序列。

二、哈夫曼编码的基本使用

(一)哈夫曼树

哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL 是最小的。

(二)压缩

压缩代码非常简单,首先用ASCII值初始化511个哈夫曼节点:

CHuffmanNode nodes[511];

for(int nCount = 0; nCount < 256; nCount++)

nodes[nCount].byAscii = nCount;

然后,计算在输入缓冲区数据中,每个ASCII码出现的频率:

for(nCount = 0; nCount < nSrcLen; nCount++)

nodes[pSrc[nCount]].nFrequency++;

然后,根据频率进行排序:

qsort(nodes, 256, sizeof(CHuffmanNode), frequencyCompare);

现在,构造哈夫曼树,获取每个ASCII码对应的位序列:

int nNodeCount = GetHuffmanTree(nodes);

构造哈夫曼树非常简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父节点了。如此循环,直到队列中只剩一个节点(树根)。

// parent node

pNode = &nodes[nParentNode++];

// pop first child

pNode->pLeft = PopNode(pNodes, nBackNode--, false);

// pop second child

pNode->pRight = PopNode(pNodes, nBackNode--, true);

// adjust parent of the two poped nodes

pNode->pLeft->pParent = pNode->pRight->pParent = pNode;

// adjust parent frequency

pNode->nFrequency = pNode->pLeft->nFrequency +

pNode->pRight->nFrequency;

这里我用了一个好的诀窍来避免使用任何队列组件。我先前就直到ASCII码只有256个,但我分配了511个(CHuffmanNode nodes[511]),前255个记录ASCII 码,而用后255个记录哈夫曼树中的父节点。并且在构造树的时候只使用一个指针数组(ChuffmanNode *pNodes[256])来指向这些节点。同样使用两个变量来操作队列索引(int nParentNode = nNodeCount;nBackNode = nNodeCount –1)。

接着,压缩的最后一步是将每个ASCII编码写入输出缓冲区中:

int nDesIndex = 0;

// loop to write codes

for(nCount = 0; nCount < nSrcLen; nCount++)

{

*(DWORD*)(pDesPtr+(nDesIndex>>3)) |=

nodes[pSrc[nCount]].dwCode << (nDesIndex&7);

nDesIndex += nodes[pSrc[nCount]].nCodeLength;

}

(nDesIndex>>3): >>3 以8位为界限右移后到达右边字节的前面

(nDesIndex&7): &7 得到最高位.

(三)解压缩

解压缩比构造哈夫曼树要简单的多,将输入缓冲区中的每个编码用对应的A SCII码逐个替换就可以了。只要记住,这里的输入缓冲区是一个包含每个ASCII 值的编码的位流。因此,为了用ASCII值替换编码,我们必须用位流搜索哈夫曼树,直到发现一个叶节点,然后将它的ASCII值添加到输出缓冲区中:int nDesIndex = 0;

DWORD nCode;

while(nDesIndex < nDesLen)

{

nCode = (*(DWORD*)(pSrc+(nSrcIndex>>3)))>>(nSrcIndex&7);

pNode = pRoot;

while(pNode->pLeft)

{

pNode = (nCode&1) ? pNode->pRight : pNode->pLeft;

相关文档
最新文档