给定一段文字,如果我们统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串"aaaxuaxz",容易得到字母 'a'、'x'、'u'、'z' 的出现频率对应为 4、2、1、1。我们可以设计编码 {'a'=0, 'x'=10, 'u'=110, 'z'=111},也可以用另一套 {'a'=1, 'x'=01, 'u'=001, 'z'=000},还可以用 {'a'=0, 'x'=11, 'u'=100, 'z'=101},三套编码都可以把原文压缩到 14 个字节。但是 {'a'=0, 'x'=01, 'u'=011, 'z'=001} 就不是哈夫曼编码,因为用这套编码压缩得到 00001011001001 后,解码的结果不唯一,"aaaxuaxz" 和 "aazuaxax" 都可以对应解码的结果。本题就请你判断任一套编码是否哈夫曼编码。

输入格式:

首先第一行给出一个正整数 N(2≤N≤63),随后第二行给出 N 个不重复的字符及其出现频率,格式如下:

c[1] f[1] c[2] f[2] ... c[N] f[N]

其中c[i]是集合{'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}中的字符;f[i]c[i]的出现频率,为不超过 1000 的整数。再下一行给出一个正整数 M(≤1000),随后是 M 套待检的编码。每套编码占 N 行,格式为:

c[i] code[i]

其中c[i]是第i个字符;code[i]是不超过63个'0'和'1'的非空字符串。

输出格式:

对每套待检编码,如果是正确的哈夫曼编码,就在一行中输出"Yes",否则输出"No"。

注意:最优编码并不一定通过哈夫曼算法得到。任何能压缩到最优长度的前缀编码都应被判为正确。

输入样例:

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

输出样例:

Yes
Yes
No
No

在完成这道题之前, 首先要对构建哈夫曼树和哈夫曼编码的框架有一定的理解。

之后再根据题目的要求进行恰当的修改。

以下代码参考   清华大学 《数据结构》(C语言版)严蔚敏 吴伟民 编著

---------------------------------------数组从1开始,0未用。

框架代码如下:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;#define MAXSIZE 100typedef struct{int weight;int parent,lchild,rchild;
}HTNode,*HuffmanTree;typedef char** HuffmanCode;void Init_HuffmanTree(HuffmanTree &HT,int n)
{int m = 2*n-1;HT = (HuffmanTree)malloc(sizeof(HTNode)*(m+1));for(int i=0;i<=m;i++){HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}
}void Select(HuffmanTree HT,int *S1,int *S2,int m)
{int minweight=10000;for(int i=1;i<m;i++){if(HT[i].weight<minweight && HT[i].parent==0){minweight = HT[i].weight;*S1 = i;}}minweight=10000;for(int i=1;i<m;i++){if(HT[i].weight<minweight && HT[i].parent==0 && i!=*S1){minweight = HT[i].weight;*S2 = i;}}}void Creat_HuffmanTree(HuffmanTree &HT,int n)
{int m = 2*n-1;int s1,s2;if(n<=1)return;for(int i=n+1;i<=m;i++){Select(HT,&s1,&s2,i);HT[s1].parent=i;HT[s2].parent=i;HT[i].lchild=s1;HT[i].rchild=s2;HT[i].weight=HT[s1].weight+HT[s2].weight;}
}void Print_HuffmanTree(HuffmanTree HT,int n)
{cout<<"index"<<"\t"<<"weight"<<"\t"<<"parent"<<"\t"<<"lchild"<<"\t"<<"rchild"<<endl;for(int i=1;i<=2*n-1;i++){cout<<i<<"\t"<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild<<"\t"<<HT[i].rchild<<endl;}
}void Creat_HuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{char *cd;int c,f,start;HC = (HuffmanCode)malloc(sizeof(char*)*(n+1));cd = (char*)malloc(sizeof(char)*n);cd[n-1]='\0';for(int i=1;i<=n;i++){start = n-1;c=i;f=HT[i].parent;while(f!=0){start--;if(HT[f].lchild==c)cd[start]='0';else cd[start]='1';c=f;f=HT[f].parent;}HC[i] = (char*)malloc(sizeof(char)*(n-start));strcpy(HC[i],&cd[start]);}free(cd);
}void Print_HuffmanCode(char ch[][MAXSIZE],HuffmanCode HC,int n)
{for(int i=1;i<=n;i++){cout<<ch[i]<<" ";cout<<HC[i]<<endl;}
}int main()
{int n;char ch[MAXSIZE][MAXSIZE];HuffmanTree HT;HuffmanCode HC;cin>>n;Init_HuffmanTree(HT,n);for(int i=1;i<=n;i++){cin>>ch[i]>>HT[i].weight;}Creat_HuffmanTree(HT,n);Creat_HuffmanCode(HT,HC,n);cout<<endl;Print_HuffmanTree(HT,n);cout<<endl;Print_HuffmanCode(ch,HC,n);cout<<endl; return 0;
}

                              

构造最优二叉树(哈夫曼树),a、b、c、d、e的权值依次为7、5、5、2、4 。 程序运行结果如上图2。

 相应的代码解释,可查看 作者:玛莱之盾_ 在bilibili上分享的视频:哈夫曼编码代码讲解

当然,这很重要。


在有了相应的基础后,我们再来看这道题目。

仔细阅读题目过后我们发现,本题的初衷并不是让我们求出哈夫曼编码。题干说:“注意:最优编码并不一定通过哈夫曼算法得到。任何能压缩到最优长度的前缀编码都应被判为正确。

这道题的解题思路为:先求出哈夫曼编码的的最短加权路径和然后再判断是不是前缀编码。两者缺一不可。

完整代码如下:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;#define MAXSIZE 100typedef struct{int weight;int parent,lchild,rchild;
}HTNode,*HuffmanTree;typedef char** HuffmanCode;void Init_HuffmanTree(HuffmanTree &HT,int n)
{int m = 2*n-1;HT = (HuffmanTree)malloc(sizeof(HTNode)*(m+1));for(int i=0;i<=m;i++){HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}
}void Select(HuffmanTree HT,int *S1,int *S2,int m)
{int minweight=10000;for(int i=1;i<m;i++){if(HT[i].weight<minweight && HT[i].parent==0){minweight = HT[i].weight;*S1 = i;}}minweight=10000;for(int i=1;i<m;i++){if(HT[i].weight<minweight && HT[i].parent==0 && i!=*S1){minweight = HT[i].weight;*S2 = i;}}}void Creat_HuffmanTree(HuffmanTree &HT,int n)
{int m = 2*n-1;int s1,s2;if(n<=1)return;for(int i=n+1;i<=m;i++){Select(HT,&s1,&s2,i);HT[s1].parent=i;HT[s2].parent=i;HT[i].lchild=s1;HT[i].rchild=s2;HT[i].weight=HT[s1].weight+HT[s2].weight;}
}void Print_HuffmanTree(HuffmanTree HT,int n)
{cout<<"index"<<"\t"<<"weight"<<"\t"<<"parent"<<"\t"<<"lchild"<<"\t"<<"rchild"<<endl;for(int i=1;i<=2*n-1;i++){cout<<i<<"\t"<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild<<"\t"<<HT[i].rchild<<endl;}
}int Creat_HuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{char *cd;int c,f,start;HC = (HuffmanCode)malloc(sizeof(char*)*(n+1));cd = (char*)malloc(sizeof(char)*n);cd[n-1]='\0';for(int i=1;i<=n;i++){start = n-1;c=i;f=HT[i].parent;while(f!=0){start--;if(HT[f].lchild==c)cd[start]='0';else cd[start]='1';c=f;f=HT[f].parent;}HC[i] = (char*)malloc(sizeof(char)*(n-start));strcpy(HC[i],&cd[start]);}int sum=0;for(int i=n+1;i<2*n;i++){sum+=HT[i].weight;}free(cd);return sum;
}void Print_HuffmanCode(char ch[][MAXSIZE],HuffmanCode HC,int n)
{for(int i=1;i<=n;i++){cout<<ch[i]<<" ";cout<<HC[i]<<endl;}
}int IsPrefix(char code[MAXSIZE][MAXSIZE],int n)  //判断是否是前缀式编码
{int flag=1; //1为是前缀式编码,0为否 for (int i = 1;i <= n;i++){int j = i+1;while (j <= n && j!=i && flag == 1){if (strstr(code[j-1], code[i-1]) != code[j-1]) //运用strstr函数 {if (j == n)j = 1;else j++;}else {flag = 0;}}if (flag == 0){break;}}return flag;
}int main()
{int n,sum_weight;char ch[MAXSIZE][MAXSIZE];HuffmanTree HT;HuffmanCode HC;cin>>n;Init_HuffmanTree(HT,n);for(int i=1;i<=n;i++){cin>>ch[i]>>HT[i].weight;}Creat_HuffmanTree(HT,n);sum_weight = Creat_HuffmanCode(HT,HC,n);//Print_HuffmanTree(HT,n);//Print_HuffmanCode(ch,HC,n);//开始读入各段哈夫曼编码int M,sum=0;cin>>M;char code[MAXSIZE][MAXSIZE];char c;for(int i=1;i<=M;i++){sum=0;for(int j=0;j<n;j++){cin>>c>>code[j];sum+=strlen(code[j])*HT[j+1].weight; //求输入的加权路径:编码长度*权重 }if(sum!=sum_weight) //先判断最短加权路径和是否相等,若不等直接No,若相等,判断是否是前缀码 {cout<<"No"<<endl;}else{if(IsPrefix(code,n)==1)cout<<"Yes"<<endl;else cout<<"No"<<endl;}}return 0;
}

下面我们来看这个代码,注意与第一个框架代码做对比

  • 首先在 Creat_HuffmanCode() 函数中,我们加入如下代码1,来求最短加权路径和。用于与主函数中(代码2)输入的哈夫曼编码作比较。
  • 依照上面的图1,找到相应的数学关系。
//代码1
int sum=0;for(int i=n+1;i<2*n;i++)  //注意从n+1开始{sum+=HT[i].weight;}return sum;
//代码2
sum=0;for(int j=0;j<n;j++){cin>>c>>code[j];sum+=strlen(code[j])*HT[j+1].weight; //求输入的加权路径:编码长度*权重 }

  • 写了 IsPrefix() 函数用于判断是否是前缀码。相应代码的思路如下:

前缀编码定义:
(字符集中)任一编码都不是其它字符的编码的前缀

例:
(1)找出下面不是前缀编码的选项
A{1,01,000,001}
B{1,01,011,010}
C{0,10,110,11}
D{0,1,00,11}

第一步:看A中的第一个数1,看看其他数有没有1开头的。没有。
第二步:看A中的第二个数01,看看其他数有没有01开头的。没有。
第三步:看看A中的第三个数000,看看其他数有没有000开头的。没有。
第四步:看看A中的第四个数001,看看其他数有没有001开头的。没有。
所以A是前缀编码。

其他选项也一样。B、C也一样。来说说D:

第一步,看D中的第一个数,找有0开头的的数,有,是00;其实到这里已经不用看了
因为D已经不是前缀编码了。
但第二个数1,是第四个数11的前缀,所以也能作为D不是前缀编码的理由。

IsPrefix() 函数中应用了strstr()函数。解释如下:


最后,题干的测试数据

4
a 4
x 2
u 1
z 1
4
a 0
x 10
u 110
z 111
a 1
x 01
u 001
z 000
a 0
x 11
u 100
z 101
a 0
x 01
u 011
z 001

河北工业大学

7-1 哈夫曼编码(实验) 最全代码解析相关推荐

  1. c语言实现哈夫曼树的创建与中序遍历以及哈夫曼编码(附详细代码)

    任务描述 本关任务:编写能对给定n个叶子结点,构建哈夫曼树,给出每个叶子结点对应编码的程序. 相关知识 哈夫曼编码和译码的基本原理 首先要构造一棵哈夫曼树.哈夫曼树的结点结构包括权值,双亲,左右孩子: ...

  2. 哈夫曼编码(Huffman)Java实现代码简化版

    这个网上发现的Huffuman编码Java实现在组织上相对简化,便于理解文件压缩过程:提取文件统计字符频度-根据字符频度创建huffman树-根据huffman树生成huffman可变字长无前缀编码- ...

  3. 哈夫曼编码(Huffman)Java实现代码

    网上找到的一个组Huffman编码Java实现代码,比较经典. 1.主类,压缩和解压 package cn.hm;import java.io.BufferedInputStream; import ...

  4. 霍夫曼编码实验matlab,哈夫曼编码 MATLAB程序

    clc clear fid=fopen( 'C:\Users\yichao\Desktop\新建文本文档.txt');%打开 txt 文件 [zimu]=fscanf(fid, '%c'); %读取二 ...

  5. 实验四-哈夫曼编码的MATLAB实现

    信息论编码实验3~9连载,更多看专栏. 哈夫曼编码MATLAB实现 一.哈夫曼编码的原理 二.哈夫曼编码的实例 三.代码及运行结果 3.1根据原理自编程序 3.2利用MATLAB内嵌函数 四.程序自评 ...

  6. 树:哈夫曼树和哈夫曼编码的详细介绍以及代码实现

    闲扯前言 哈夫曼编码的代码实现对于初学数据结构的同学可能会有些困难,没有必要灰心,其实没啥,学习就犹如攀登一座又一座的山峰,每当我们攻克一个难点后,回首来看,也不过如此嘛.我们要做的就是不断的去攀越学 ...

  7. 哈夫曼编码原理与Python实现代码(附手动推导过程原稿真迹)

    哈夫曼编码依据字符出现概率来构造异字头(任何一个字符的编码都不是其他字符的前缀)的平均长度最短的码字,通过构造二叉树来实现,出现频次越多的字符编码越短,出现频次越少的字符编码越长.为了演示哈夫曼编码原 ...

  8. 使用Java实现哈夫曼编码(Huffman Coding)

    文章目录 (一)需求分析 (二)构建哈夫曼树 (三)构建哈夫曼编码 (四)哈夫曼编码的解码 (五)哈夫曼编码压缩的原理 (六)总结 (七)Java代码实现哈夫曼树:构建节点类&二叉树类 (八) ...

  9. Java如何实现哈夫曼编码

    哈夫曼树 既然是学习哈夫曼编码,我们首先需要知道什么是哈夫曼树:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tre ...

最新文章

  1. 麻省、北大、清华等顶尖高校与企业 20 位强化学习专家齐聚,RLChina 2021 强化学习暑期课免费报名啦!
  2. 青龙面板多容器教程、一键多容器
  3. chrome浏览器本地文件支持ajax请求的解决方法
  4. 洛谷 1072 Hankson 的趣味题——质因数界限讨论
  5. 深度学习之tensorflow环境搭建
  6. dlgdata.cpp错误提示 解决方案
  7. es6 作为属性名的 Symbol
  8. nginx学习文档之二 配置负载均衡-windows配置负载均衡
  9. 修改WebBrowser控件的内核解决方案
  10. JAVA零为扩展_与Java的初遇——数据类型扩展
  11. 国都企信通短信平台发送手机短信的python脚本一例
  12. 用gambit学博弈论---零和博弈
  13. 从零开始的车牌识别课题设计(一)
  14. PHP笔记03-数组1
  15. 网易2017年春招笔试题记录
  16. Mac下安装Hadoop
  17. python:大球吃小球
  18. 到底还要学什么才能成为2020年前端架构师!?(用图说话)
  19. linux 设置邮件提醒,linux 定时邮件提醒
  20. 【Django 开发】面试招聘信息网站(用户登录注册投在线递简历)

热门文章

  1. Jmeter+badboy自动化测试——Badboy基础操作
  2. Component name “xxx“ should always be multi-word. vue3项目eslint报错
  3. 四平方和定理 leetcode279 c++
  4. (4.3.1.11)微信扫描二维码无法下载apk文件解决办法
  5. Diabetes 糖尿病及其并发症.|2021/1/25(未完待续)
  6. Android面试题及答案
  7. 1.数组简介:什么是数组???数组有哪四要素???
  8. Android-银联支付开发
  9. MySQL [1093] You can‘t specify target table ‘titles_test‘ for update in FROM clause
  10. 第一次.......