wordpress首页美化,百度网络优化,ccd设计公司官网,网站建设规划结构2019.12.15更正Best函数样本数据初始化问题并且对代码添加了注释; 2020.11.17更正#xff1a;题目说明#xff1b; 原贴发于2019.11.22 注意#xff1a;本题不是哈夫曼编码裸题#xff0c;学习哈夫曼编码的同学不要过度依赖本题算法#xff0c;只有参考价值#xff1b; 给…2019.12.15更正Best函数样本数据初始化问题并且对代码添加了注释; 2020.11.17更正题目说明 原贴发于2019.11.22 注意本题不是哈夫曼编码裸题学习哈夫曼编码的同学不要过度依赖本题算法只有参考价值 给定一段文字如果我们统计出字母出现的频率是可以根据哈夫曼算法给出一套编码使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串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” 都可以对应解码的结果。本题就请你判断任一套编码是否哈夫曼编码。
输入格式 首先第一行给出一个正整数 N2≤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思路这道题应该是树这块比较综合的一道题目的注意很重要他直接给了我们一个方向最优编码不一定是由哈夫曼算法得到的并且哈夫曼编码的结果不是惟一的这就意味着构造哈夫曼树进行哈夫曼编码的方式是不适用了。 经过老师的提醒这道题应该这样做1判断题目所给的是不是前缀编码即翻译过程中会不会产生歧义2编码是不是最优的。 为此我写了两个函数
bool judge(int start,int end) //判断是否是前缀编码
{for (int istart;iend;i){for (int jstart;jend;j){if (ij){continue;}if (strlen(in[i].code)strlen(in[j].code)){continue;}int k;for (k0;kstrlen(in[i].code);k){if (in[i].code[k]!in[j].code[k]){break;}}if (kstrlen(in[i].code)){return false;}}}return true;
}bool best(int start,int end) //哈夫曼编造树求加权最短路径看是否相等
{Create self[131];int it0; //输入的带权路径和 int m2*n-1;for (int istart;iend;i){itin[i].weight*in[i].P;}for (int i0;in;i){self[i].parentself[i].lchildself[i].rchild-1;self[i].weightin[start].P;}for (int in;im;i){self[i].parentself[i].lchildself[i].rchild-1;self[i].weight0;}int x1,x2;for (int in;im;i){Find(self,x1,x2,i);
//printf(%d %d %d %d\n,x1,x2,self[x1].weight,self[x2].weight);self[i].lchildx1;self[i].rchildx2;self[i].weightself[x1].weightself[x2].weight;self[x1].parenti;self[x2].parenti;}int itt0;for (int in;im;i){
//printf(##%d##\n,self[i].weight);ittself[i].weight;}
//printf(***%d %d***\n,it,itt);if (ititt) return true;else return false;
}void Find(Create *self,int *x1,int *x2,int count) //寻找最小值下标和次小值下标
{
//printf(//%d//\n,count);int min130,pmin130;self[min].weight10000001;for (int i0;icount;i){if (self[i].parent-1self[i].weightself[min].weight){pminmin;mini;}else if (self[i].parent-1self[i].weightself[pmin].weight){pmini;}}*x1min;*x2pmin;
}当所给的代码符合这两个条件时就是正确的哈夫曼编码。
#include bits/stdc.h
#include queue
using namespace std;
int n;
struct knot
{char name; //字符 int P; //出现概率int weight; //输入串长 char code[65]; //编码
}a[6400],in[63001]; //a用name和Pin用name、weight(码的长度)和code
typedef struct knoto
{int parent;int lchild,rchild;int weight;
}Create;
bool judge(int start,int end); //判断是否有歧义,start是起始下标end是结束下标
bool best(int start,int end); //获取最优输入
void Find(Create *self,int *x1,int *x2,int count);
int main()
{int k,itt;scanf(%d,n);for (int i0;in;i){getchar();scanf(%c%d,a[i].name,a[i].P);}scanf(%d,k); //组数 for (int i0;ik*n;i){getchar();scanf(%c %s,in[i].name,in[i].code);in[i].weightstrlen(in[i].code);in[i].Pa[i%n].P;if (i%nn-1) //一组输入完毕,i是从0开始的 {if (judge(i-n1,i)best(i-n1,i)){printf(Yes\n);}else{printf(No\n);}}}return 0;
}bool judge(int start,int end) //此函数的功能是判断一组输入的编码是不是前缀编码即在
{ //组内比较 for (int istart;iend;i){for (int jstart;jend;j){if (ij) //如果是一个码不用比 {continue;}if (strlen(in[i].code)strlen(in[j].code)) //i的码比j的码长i不可能是j编码的一部分 {continue;}int k;for (k0;kstrlen(in[i].code);k) //正式进入比较 {if (in[i].code[k]!in[j].code[k]){break; //注意这里不能直接写return true,否则会犯比较不足的错误 }}if (kstrlen(in[i].code)) //顺利结束表明这不是前缀编码即有重复 {return false;}}}return true; //坎坷之后才是真正的编码
}bool best(int start,int end) //哈夫曼编造树求加权最短路径看是否相等
{Create self[131];int it0; //输入的带权路径和 int m2*n-1;for (int istart;iend;i) //计算输入的带权路径和 {itin[i].weight*in[i].P;}for (int i0;in;i) //对已有的样本数据进行存储静态实现 {self[i].parentself[i].lchildself[i].rchild-1;self[i].weighta[i].P;}for (int in;im;i) //这是对空间进行初始化没有孩子没有双亲没有权值 {self[i].parentself[i].lchildself[i].rchild-1;self[i].weight0;}int x1,x2; //保存权值最小值和次小值的数组下标 for (int in;im;i) //从第一个不是原有数据的储存空间开始 {Find(self,x1,x2,i);self[i].lchildx1;self[i].rchildx2; //孩子初始化 self[i].weightself[x1].weightself[x2].weight; //权值刷新 self[x1].parenti; //双亲刷新 self[x2].parenti;}int itt0; //计算 for (int in;im;i){ittself[i].weight;}if (ititt) return true;else return false;
}void Find(Create *self,int *x1,int *x2,int count) //可用stl最小堆优化
{int min130,pmin130; //需要保证节点不会累加到此处 self[min].weight10000001;for (int i0;icount;i){if (self[i].parent-1self[i].weightself[min].weight){pminmin;mini;}else if (self[i].parent-1self[i].weightself[pmin].weight){pmini;}}*x1min;*x2pmin;
}