C++哈夫曼树和哈夫曼编码详解-创新互联

  理论部分

成都创新互联成立以来不断整合自身及行业资源、不断突破观念以使企业策略得到完善和成熟,建立了一套“以技术为基点,以客户需求中心、市场为导向”的快速反应体系。对公司的主营项目,如中高端企业网站企划 / 设计、行业 / 企业门户设计推广、行业门户平台运营、重庆APP开发公司手机网站制作设计、微信网站制作、软件开发、成都服务器托管等实行标准化操作,让客户可以直观的预知到从成都创新互联可以获得的服务效果。

先补充一个概念,什么是路径长度?

从树中一个节点到另一个节点之间的分支构成这两个节点之间的路径,路径上的分支数目称作路径长度。而一般不带权的单个路径长度默认为1,所以可以认为节点数为n的树的路径长度为n-1。

哈夫曼树的定义是带权路径长度最短的树,也叫最优二叉树。换种更好的理解方式,就是一棵特殊的二叉树,而这棵树的叶子节点到根节点的带权路径都是尽可能最短的

如下图:

树a的路径长度就是7*2+5*2+2*2+4*2=36。

树b的路径长度就是7*3+5*3+2*1+4*2=46

树c的路径长度就是7*1+5*2+2*3+4*3=35

明显,树c的路径长度最小,但它是最优二叉树吗?

想要验证树c是不是哈夫曼树,就得从定义出发。带权路径长度最小,那么很容易想到,权重大的路径所在的层数一定偏小,而权重小的路径层数就偏小。那么很容易联想到,把权重小的路径先找出来并放在下面。经过总结可得出以下结论:

(1)根据给定的n个权值 {w1,w2,⋯,wn} 构成n棵二叉树的集合 F={T1,T2,⋯,Tₙ}, 其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树均空。
(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复(2)和(3),直到F只含一棵树为止。这棵树便是赫夫曼树。

哈夫曼编码与前缀编码:
前缀编码:如果在一个编码方案中, 任一个编码都不是其他任何编码的前缀(最左子串),如称编码是前缀编码。前缀编码可以保证对压缩文件进行解码时不产生二义性,确保正确解码。

哈夫曼编码:对一棵具有n个叶子的哈夫曼树,若对树中的每个左分支赋予0,右分支赋予1,则从根到每个叶子的路径上,各分支的赋值分别构成一一个二进制串,该二进制串就称为哈夫曼编码。
哈夫曼编码是前缀编码:哈夫曼编码是根到叶子路径上的编码序列,由树的特点知,若路径A是另一条路径B的最左部分,则B经过了A.则A的终点一定不是叶子,而哈夫曼编码对应路径的终点一定为叶子,因此,任一哈夫曼码都不会与任意其他哈夫曼编码的前缀部分完全重叠,因此哈夫曼编码是前缀编码。

哈夫曼编码是最优前缀编码:对于包括n个字符的数据文件,分别以它们的出现次数为权值构造哈夫曼树,则利用该树对应的哈夫曼编码对文件进行编码,能使该文件压缩后对应的二进制文件的长度最短。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

实践部分

以数据结构哈夫曼树和哈夫曼编码基础题为例

请构建夫曼树和哈夫曼编码的实现,对于输入的(n=8)个字符和对应的概率,生成其对应的哈夫曼编码。

参考输入如下:

"a","b","c","d","e","f","g","h"

0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.1

参考输出如下:

a:        1010

b:        00

c:        10000

d:        1001

e:        11

f:        10001

g:        01

h:        1011

首先是哈夫曼树的定义,一般要求记录节点的权重,双亲节点,左、右子女节点。

typedef struct{
    int weight;//权重
    int parents, Lchild, Rchlid;
}thnode,HuffmanTree[1000];

再是哈夫曼树的构建,先对前n个(叶子)节点进行处理,记录它们的权重,其他信息记为0。再对后面构建哈夫曼树形成的过程节点(容易得知哈夫曼树的节点个数为2*n-1个,所以过程节点有n-1个)的所有信息记为0。在根据理论部分的规律,进行哈夫曼树的构建。

void Huffman_Creat(HuffmanTree ht,int w[],int n)//n为叶子结点个数
{
    //先处理前n个节点
    int m = 2 * n - 1;//树的总结点数
    int i,s1,s2;
    for (i = 1; i<= n;i++)
    {
        ht[i].weight = w[i];
        ht[i].Lchild = 0;
        ht[i].Rchlid = 0;
        ht[i].parents = 0;
    }
    //再处理后面的过程节点
    for (i = n+1; i<= m;i++)
    {
        ht[i].weight = 0;
        ht[i].Lchild = 0;
        ht[i].Rchlid = 0;
        ht[i].parents = 0;
    }
    //最后再按规律构建哈夫曼树
    for (i = n + 1; i<= m;i++)
    {
        select(ht, i - 1, &s1, &s2);//寻找双亲节点不为0的权重最小的2个节点
        ht[i].weight = ht[s1].weight + ht[s2].weight;
        ht[i].Lchild = s1;
        ht[i].Rchlid = s2;
        ht[s1].parents = i;
        ht[s2].parents = i;
    }
}

select函数就是寻找双亲节点不为0的权重最小的2个节点。

void select(HuffmanTree ht,int end,int*s1,int*s2)
{
    int min1, min2;//最小和第二小
    int i = 1;
    while(ht[i].parents!=0 && i<=end)
        i++;
    min1 = ht[i].weight;
    *s1 = i;

    i++;
    while(ht[i].parents!=0 && i<=end)
        i++;
    if(ht[i].weight=min1 && ht[j].weight

哈夫曼树构建完成后,就是哈夫曼编码的建立。利用定义中的parent,可以从叶子节点寻找回根节点,跟从根节点遍历到叶子节点的方法相比,不但减少代码量,还方便记录过程编码。

typedef char* HuffmanCode[1000];
void HuffmanCode_Creat(HuffmanTree ht, HuffmanCode hc,int n)
{
    char *s;
    int start;
    int i, c, p;
    s = (char *)malloc(n * sizeof(char));//创建临时数组
    s[n - 1] = '\0';//反向存入s数组中,方便后续输出
    for (i = 1; i<= n;i++)
    {
        start = n - 1;
        c = i;//存储当前节点
        p = ht[i].parents;//存双亲
        while(p!=0)//只有根节点的双亲节点为0
        {
            --start;
            if(ht[p].Lchild==c)
                s[start] = '0';
            else
                s[start] = '1';
            c = p;
            p = ht[p].parents;//向上寻找双亲,直到为根节点
        }
        hc[i] = (char *)malloc((n - start) * sizeof(char));
        strcpy(hc[i], &s[start]);//将每次的结果存入hc数组
    }
    free(s);
}

结合本题要求,可得完整代码为:

请代入个人思考,本代码故意改了部分,输出一定是错误的!!!

请代入个人思考,本代码故意改了部分,输出一定是错误的!!!

请代入个人思考,本代码故意改了部分,输出一定是错误的!!!

#includeusing namespace std;
typedef struct{
	char id;
    double weight;//权
    int parents, Lchild, Rchlid;
}thnode,HuffmanTree[1000];
void select(HuffmanTree ht,int end,int*s1,int*s2)
{
    double min1, min2;
    int i = 1;
    while(ht[i].parents!=0 && i<=end)
        i++;
    min1 = ht[i].weight;
    *s1 = i;

    i++;
    while(ht[i].parents!=0 && i<=end)
        i++;
    if(ht[i].weight=min1 && ht[j].weight>n;
    for (int i=1;i<=n;i++)
    	cin>>ppp[i];
    for (int i=1;i<=n;i++)
    	cin>>w[i];
    HuffmanTree ht;
    Huffman_Creat(ht, w, n);
	for (int i=1;i<=n;i++)
    	ht[i].id=ppp[i];
    HuffmanCode hc;
    HuffmanCode_Creat(ht,hc, n);
    for (i = 1; i<=n; i++)
      {
          printf("%c: %s\n", ht[i].id,hc[i]);
      }
    return 0;
}

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


文章题目:C++哈夫曼树和哈夫曼编码详解-创新互联
URL网址:http://pwwzsj.com/article/ggspg.html