Huffman编码 POJ 1521
2015-09-07 10:21
281 查看
写题解之前真的想先默念三声:傻逼题傻逼题傻逼题。
这是我第一次在博客里爆粗口,因为这道题黑了我20次提交记录,满页红。
这道题很水很水,连模板题都要算不上了,为什么要写到这里呢,因为这是我第一次了解到Huffman编码。
题意:题目描述的很复杂,但是搜题意的时候发现大家基本都说的很简洁,就是让你求Huffman树的带权路径长度。然后输出原字符串占用空间(原每个字符占用8位),压缩后占用空间(带权路径长度——当直接用出现次数表示频率时),以及两个值之比。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
详情可以去看:维基百科:霍夫曼编码,在这里我只写我的理解了。
-------------------------------------------------------------------------------------
Huffman编码是对数据进行无损压缩的一种编码方式,它按照每个字符的出现频率进行不同的编码。比如说英文单词中's'的出现频率很高,那么很有可能用1位来存储's',而'j'的出现频率很低,那么可能用原本长度8位或者更多来表示'j'。平均下来,是节省空间的,当然均匀分布的数据是不变的。
如果要得到Huffman编码就要构建Huffman树,这个过程并不复杂,最终态的树的叶子结点都是被编码的字符,而且是一颗二叉树。构造过程是,首先要知道每种字符的出现频率,表示为节点权值;每个节点的权值都表示这个节点以及此节点的子树的权值和,一开始的时候,就是n种字符的n棵单个节点的树,每次我们找出两棵权值(即根节点权值)最小的树合并,新建立一个父节点,被合并的两棵树分别是左右子树,直到只剩下一棵树。
那么最后剩下的一棵树就是Huffman树,我们可以以走向左子树记为0,走向右子树记为1,从根节点走到每个叶子结点,就可以得到压缩后的每种字符的编码。
找权值最小的过程可以用C++ STL里的priority_queue解决,取出最小的两个,合并,加入。
-------------------------------------------------------------------------------------
对于这道题,甚至不需要构造出来Huffman树,因为它只需要带权路径长度,我们可以仅仅在每次把合并成的新的“树”加入到堆里时把它的权值加到ans里即可。就相当于把每个非叶子节点的权值求和,最后就是带权路径长度。没有必要非把树建出来,再DFS求每个节点深度。
为什么WA了将近20次,要小心输入中可能有空格以及汉字。空格很容易料到,但是汉字就不好说了,20次很大一部分是没有注意到输入中还有汉字。
用string不能用char数组,至少我是栽在了char数组上,输入一有汉字就会挂。读入最好用getline。
另外还要注意输入中可能只有一种字符,那么本身就只有一棵树,大部分写的构建Huffman树的停止条件都是堆里只有一个元素,这种情况加个特判就可以了。
真是悲伤的经历。
这是我第一次在博客里爆粗口,因为这道题黑了我20次提交记录,满页红。
这道题很水很水,连模板题都要算不上了,为什么要写到这里呢,因为这是我第一次了解到Huffman编码。
题意:题目描述的很复杂,但是搜题意的时候发现大家基本都说的很简洁,就是让你求Huffman树的带权路径长度。然后输出原字符串占用空间(原每个字符占用8位),压缩后占用空间(带权路径长度——当直接用出现次数表示频率时),以及两个值之比。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
详情可以去看:维基百科:霍夫曼编码,在这里我只写我的理解了。
-------------------------------------------------------------------------------------
Huffman编码是对数据进行无损压缩的一种编码方式,它按照每个字符的出现频率进行不同的编码。比如说英文单词中's'的出现频率很高,那么很有可能用1位来存储's',而'j'的出现频率很低,那么可能用原本长度8位或者更多来表示'j'。平均下来,是节省空间的,当然均匀分布的数据是不变的。
如果要得到Huffman编码就要构建Huffman树,这个过程并不复杂,最终态的树的叶子结点都是被编码的字符,而且是一颗二叉树。构造过程是,首先要知道每种字符的出现频率,表示为节点权值;每个节点的权值都表示这个节点以及此节点的子树的权值和,一开始的时候,就是n种字符的n棵单个节点的树,每次我们找出两棵权值(即根节点权值)最小的树合并,新建立一个父节点,被合并的两棵树分别是左右子树,直到只剩下一棵树。
那么最后剩下的一棵树就是Huffman树,我们可以以走向左子树记为0,走向右子树记为1,从根节点走到每个叶子结点,就可以得到压缩后的每种字符的编码。
找权值最小的过程可以用C++ STL里的priority_queue解决,取出最小的两个,合并,加入。
-------------------------------------------------------------------------------------
对于这道题,甚至不需要构造出来Huffman树,因为它只需要带权路径长度,我们可以仅仅在每次把合并成的新的“树”加入到堆里时把它的权值加到ans里即可。就相当于把每个非叶子节点的权值求和,最后就是带权路径长度。没有必要非把树建出来,再DFS求每个节点深度。
为什么WA了将近20次,要小心输入中可能有空格以及汉字。空格很容易料到,但是汉字就不好说了,20次很大一部分是没有注意到输入中还有汉字。
用string不能用char数组,至少我是栽在了char数组上,输入一有汉字就会挂。读入最好用getline。
另外还要注意输入中可能只有一种字符,那么本身就只有一棵树,大部分写的构建Huffman树的停止条件都是堆里只有一个元素,这种情况加个特判就可以了。
真是悲伤的经历。
#include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> #include <queue> using namespace std; string s; priority_queue <int, vector<int>, greater<int> > q; int main() { while(getline(cin, s) && s != "END"){ int t = 1; sort(s.begin(), s.end()); for(int i = 1; i < s.length(); i++){ if(s[i] != s[i-1]){ q.push(t); t = 1; } else t++; } q.push(t); if(q.size() == 1) { printf("%d %d 8.0\n", s.length()*8, s.length()); q.pop(); continue; } int ans = 0; while(q.size() > 1){ int a = q.top(); q.pop(); int b = q.top(); q.pop(); q.push(a+b); ans += a+b; } q.pop(); printf("%d %d %.1lf\n", s.length()*8, ans, (double)s.length()*8.0/(double)ans); } }
相关文章推荐
- Android EditText默认弹出和默认关闭输入法的解决方案
- Spring4+Hibernate4整合经验:事务配置导致的java.lang.NoSuchMethodError异常
- 减资程序&案例
- iCloud的使用
- fatal error LINK1123:failure during conversion to COFF:file invalid or corrupt
- Hadoop Yarn 框架原理及运作机制
- Processing 练习(5)- Random bubbles fade out!!
- 关于JavaScript中function的两种创建方式的解析
- Meteor全栈开发平台 - 不仅仅是前端
- 微信app支付
- 进程通信-dbus(1)
- C3P0连接池配置信息记录备查
- dos下查看磁盘内存信息
- 常见的传输码型
- BUFFER CACHE之调整buffer cache的大小
- linux进程调度原理
- MySQL cmake安装
- IOS ARC项目使用非ARC文件
- 微信公众平台的后台开发流程(一)
- 高效学习Android动画