哈夫曼编码

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

设计哈夫曼编码

【问题描述】

根据给定字符的使用频率,为其设计哈夫曼编码。

【基本要求】

●功能:求出n个字符的哈夫曼编码。

●输入:输入n个字符和字符在电文中的使用频率。

●输出:n个字符的哈夫曼编码。

【测试数据】

输入字符集{a,b,c,d,e,f,g,h}的使用频率{5,29,7,8,14,23,3,11}(扩大100倍),预期的输出为这8个字符的哈夫曼编码a:0001;b:10;c:1110;d:1111;e:110;f:01;h:001 。【数据结构】

哈夫曼树中没有度数为1的分支结点,有n个叶子结点的哈夫曼树中共有2n-1个结点,可以用大小为2n-1的数组来存储哈夫曼树中的结点。在哈夫曼算法中,对每个结点,既需要知道双亲的信息,又需要知道其孩子结点的信息,因此,其存储结构为:#define n 8 /*叶子数目*/

#define m 2*n-1 /*结点总数*/

typedef struct

{int weight; /*结点的权值*/

int lchild, rchild, parent; /*左、右孩子及双亲的下标*/

}htnode;

typedef htnode humffmantree[m+1]; /* humffmantree结构数组类型,其0号单元不用*/ humffmantree ht;

其中,每个结点包括4个域,weight域存放结点的权值;lchild、rchild域分别为结点的左右孩子在数组中的下标,叶子结点的这两个域的值为0;parent域是结点的双亲在数组中的下标。这里设置parent域不仅仅是为了使涉及双亲的运算方便,它的主要作用是根和非根结点。之所以要区分根与非跟结点,是因为在当前森林中合并两棵二叉树时,必须在森林的所有结点中先取两个权值最小的根结点,因此,有必要为每个结点设置一个标记以区分根和非跟结点

为实现方便,将n个叶子结点集中存储在前面的n个位置(下标为1-n),后面n-1个位置存储n-1个非叶子结点。

把编码存储在一个数组code中,哈夫曼树中每个叶子结点的哈夫曼编码长度不同,因字符集的大小为n,因此编码的长度不会超过n,数组code的大小可设为n+1(下标为0的单元不用)。编码的存储结构为:

typedef struct

{char ch; /*存储字符*/

char code[n+1]; /*存放编码位串*/

}codenode;

typedef codenode haffmancode[n+1]; /* haffmancode 是结构数组类型,其0号单元不用,存储哈夫曼编码*/

【算法思想】

首先由以下3步求哈夫曼树。

①初始化。将ht[1..m]中每个结点的lchild,rchild,parent域全置为零。

②输入。读入n个叶子结点的权值存放于ht数组的前n个位置中,它们是初始森林中n个孤立的根结点的权值。

③合并。对初始森林中的n棵二叉树进行n-1次合并,每合并一次产生一个新结点,所产生的新结点依次存放到数组ht的第i(n

第一步:在当前森林ht[1..i-1]的所有结点中,选择权值最小的两个根结点ht[p1]和ht[p2](p1为权值最小的根结点的序号,p2为权值次小的根结点的序号)进行合并 1≤p1,p2≤i-1;

第二步:将根为ht[p1]和ht[p2]的两棵二叉树作为左右子树合并为一棵新的二叉树,新二叉树的根存放在ht[i]中,因此,将ht[p1]和ht[p2]的双亲域置为i,并且新二叉树根结点的权值应为其左右子树权值的和,即ht[i].weight=ht[p1].weight+ht[p2].weight,新二叉树根结点的左、右孩子分别为p1和p2, 即ht[i].lchild=p1,ht[i].rchild=p2(权值小的作为左孩子)。

【注意】由于合并以后,ht[p1]和ht[p2]的双亲域的值为i,不再是0,这说明它们已不再是根,在下一次合并时不会被选中。

求得哈夫曼树后,再按下述方法求哈夫曼编码。依次以叶子ht[i](1≤i≤n)为出发点,向上回溯至根为止。用临时数组cd存放求得的哈夫曼编码,用变量start 指示每个叶子结点的编码在数组cd中的起始位置,实际的编码从cd[start]到cd[n]。对于当前叶子结点ht[i],将start 置初值为n,找其双亲结点ht[f],若当前结点是双亲结点的左孩子结点,则cd数组的相应位置置为0;若当前结点是双亲结点的右孩子结点,则cd数组的相应位置置为1;然后start减1。再对双亲结点进行同样的操作,直到根结点为止,让start的值加1,指向编码的开始位置。实际的编码从cd[start]到cd[n]。最后把编码复制到数组hcd[i]的code 域中。

【模块划分】

一共设计5个模块:

⑴初始化哈夫曼树函数inithuffmantree() ;

⑵输入权值函数inputweight();

⑶选择两个权值最小的根结点函数selectmin();

⑷构造哈夫曼树函数createhuffmantree();

⑸求哈夫曼编码函数huffmancode();

【源程序】

#include

#include

#include

/* Huffman 树的存储结构*/

#define n 8 /*叶子数目根据需要设定*/

#define m 2*n-1 /* Huffman 树中结点总数*/

typedef struct

{int weight; /*结点的权值*/

int lchild,rchild,parent; /*左、右孩子及双亲的下标*/

}htnode;

typedef htnode huffmantree[m+1];/* huffmantree是结构数组类型,其0号单元不用,存储哈夫曼树*/ typedef struct

{char ch; /*存储字符*/

char code[n+1]; /*存放编码位串*/

}codenode;

typedef codenode huffmancode[n+1];/*huffmancode是结构数组类型,其0号单元不用,存储哈夫曼编码*/

void inithuffmantree(huffmantree ht) /*初始化哈夫曼树函数inithuffmantree()*/

{int i;

for(i=0;i<=m;i++)

{ht[i].weight=0;

相关文档
最新文档