2014.07.06 16:47

简介:

  给定一段有固定符号集合S构成的文本T,集合S中总共有n种符号。如果对于每种符号,使用一种不同的由‘0’和‘1’构成的位字符串来代替,比如:

    ‘a’->‘01’

    ‘c’->'101'

    'd'->‘11’

    ...

  例如,文本“acd”经过这种编码就变成了“0110111”。

  这样,就可以把文本T中的符号全部替换为‘0’‘1’构成的二进制串,这样就能以二进制文件的形式保存信息了。并且,一个ASCII字符默认占用一个字节,也就是8位。但使用这种不定长的编码方式一个字符占用的位数可能小于8位,于是可能达到压缩数据的效果。Huffman编码的规则,就是通过选定合适的编码,使得这段文本经过编码转换后的二进制串的长度最短。

图示:

  用算法描述Huffman编码的过程还是比较简单的:

  1. 定义键值对<字符, 出现频率>,比如<a, 12>表示a字符出现了12次。

  

  2. 每次选出出现频率最低的两个字符,组合成一个字符(字符当然不能组合,但频率是可以相加的),重新放入候选集中。

  

  3. 这个组合的过程,其实就是构建二叉树的过程

  

  新结点的频率等于两个子节点的频率之和,而新节点上对应的字符没有实际意义,所以我们姑且标记为‘?’。

  每经过一轮这样的操作,我们取出两个结点,放回一个结点,所以要经过n-1轮才能得到一棵完整的树,比如这样:

  

  上图中给出了这棵树对应的字符编码方式,其实每个字符的编码对应于从根结点到叶结点的路径,‘0’向左,‘1’向右。

  由于组合两个结点时,左右次序可以调换,因此同一套文本与字符可以构建出2^(n-1)种的Huffman树。任何一种的效果都是相同的,目的只有一个:压缩数据。

  如何每次选出最小的两个呢?最小堆。

  问题是:为什么每次选出最小的,结果就是最好的呢?贪婪。

实现:

 // A simple illustration for
#include <iostream>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std; // The character statistics type
typedef unordered_map<char, int> StatType;
// The character encoding type
typedef unordered_map<char, string> EncodeType; struct TreeNode {
char ch;
int weight;
TreeNode *left;
TreeNode *right; TreeNode(char _ch, int _weight): ch(_ch), weight(_weight),
left(nullptr), right(nullptr) {}
}; struct GreaterFunctor {
bool operator () (const TreeNode *x, const TreeNode *y) {
return x->weight > y->weight;
}
}; void deleteTree(TreeNode *&root)
{
if (root == nullptr) {
return;
} else {
deleteTree(root->left);
deleteTree(root->right);
delete root;
root = nullptr;
}
} void calculateEncoding(const TreeNode *root, EncodeType &encoding, string &path)
{
if (root == nullptr) {
return;
} if (root->ch != '\0') {
encoding[root->ch] = path;
return;
} path.push_back('');
calculateEncoding(root->left, encoding, path);
path.pop_back(); path.push_back('');
calculateEncoding(root->right, encoding, path);
path.pop_back();
} void huffmanEncoding(const StatType &statistics, EncodeType &encoding)
{
priority_queue<TreeNode *, vector<TreeNode *>, GreaterFunctor> pq; int n; n = ;
for (StatType::const_iterator sta_it = statistics.begin();
sta_it != statistics.end(); ++sta_it) {
pq.push(new TreeNode(sta_it->first, sta_it->second));
++n;
} TreeNode *p1, *p2, *p3;
int i;
for (i = ; i < n - ; ++i) {
p1 = pq.top();
pq.pop();
p2 = pq.top();
pq.pop(); p3 = new TreeNode('\0', p1->weight + p2->weight);
p3->left = p1;
p3->right = p2;
pq.push(p3);
} TreeNode *root = pq.top();
pq.pop(); string code = "";
calculateEncoding(root, encoding, code);
deleteTree(root);
} int main()
{
int i, n;
string s;
int weight;
StatType statistics;
EncodeType encoding; while (cin >> n && n > ) {
for (i = ; i < n; ++i) {
cin >> s >> weight;
statistics[s[]] = weight;
}
huffmanEncoding(statistics, encoding); for (EncodeType::const_iterator enc_it = encoding.begin();
enc_it != encoding.end(); ++enc_it) {
cout << enc_it->first << ':' << enc_it->second << endl;
}
cout << endl; statistics.clear();
encoding.clear();
} return ;
}

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Huffman编码的更多相关文章

  1. 数据结构与算法分析——C语言描述 第三章的单链表

    数据结构与算法分析--C语言描述 第三章的单链表 很基础的东西.走一遍流程.有人说学编程最简单最笨的方法就是把书上的代码敲一遍.这个我是头文件是照抄的..c源文件自己实现. list.h typede ...

  2. 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)

    #include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...

  3. C语言学习书籍推荐《数据结构与算法分析:C语言描述(原书第2版)》下载

    维斯 (作者), 冯舜玺 (译者) <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行 ...

  4. 《数据结构与算法分析——C语言描述》ADT实现(NO.00) : 链表(Linked-List)

    开始学习数据结构,使用的教材是机械工业出版社的<数据结构与算法分析——C语言描述>,计划将书中的ADT用C语言实现一遍,记录于此.下面是第一个最简单的结构——链表. 链表(Linked-L ...

  5. 《数据结构与算法分析-Java语言描述》 分享下载

    书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...

  6. 读书笔记:《数据结构与算法分析Java语言描述》

    目录 第 3 章 表.栈和队列 3.2 表 ADT 3.2.1 表的简单数组实现 3.2.2 简单链表 3.3 Java Collections API 中的表 3.3.1 Collection 接口 ...

  7. 《数据结构与算法分析:C语言描述_原书第二版》CH3表、栈和队列_reading notes

    表.栈和队列是最简单和最基本的三种数据结构.基本上,每一个有意义的程序都将明晰地至少使用一种这样的数据结构,比如栈在程序中总是要间接地用到,不管你在程序中是否做了声明. 本章学习重点: 理解抽象数据类 ...

  8. 【数据结构与算法分析——C语言描述】第二章总结 算法分析

    算法 算法(algorithm)是为求解一个问题需要遵循的.被清楚地指定的简单指令的集合. 数学基础 四个定义: 1.大O表示法: 如果存在正常数 c 和 n0 使得当 N ≥ n0时,T(N) ≤ ...

  9. 【数据结构与算法分析——C语言描述】第一章总结 引论

    这一章主要复习了一些数学知识,像指数.对数.模运算.级数公式:还有2种证明方法,归纳假设法和反证法.所幸以前学过,重新拾捡起来也比较轻松. 简要地复习了递归,提出了编写递归例程的四条基本法则: 基准情 ...

随机推荐

  1. 使用Atom编写Makedown

    Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器. Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. 作为一个Gi ...

  2. C sharp #003# 面向对象编程基本构件

    饮水思源:金老师的自学网站 索引 类的属性 简化字段/属性的初始化 命名空间 程序集 类的属性 字段+get/set方法=属性 (之前都是把字段和属性混着用..) 经典写法: using System ...

  3. 20145238-荆玉茗 《Java程序设计》第四次实验报告

    20145238<Java程序设计>第四次实验报告 实验四 Android环境搭建 实验内容 1.搭建Android环境 2.运行Android 3.修改代码,能输出学号 实验步骤 搭建A ...

  4. Java中阻塞队列的使用

    http://blog.csdn.net/qq_35101189/article/details/56008342 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如 ...

  5. Linux查看CPU和内存使用情况[转]

    在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要.在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况.运行 top 命令后,CPU 使用状态会 ...

  6. javascript原生API总结

    一.查找: getElementById() 方法返回带有指定 ID 的元素(唯一): getElementsByTagName() 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组). ...

  7. sudo 密码超时时间

    Centos 没有默认超时时间,所以用一次sudo就需要输入密码. vi /etc/sudoers 添加下面的内容,2表示分钟数(看自己需求更改). Defaults timestamp_timeou ...

  8. Vim---配置实用的.vimrc文件

    配置自己电脑的vim,配置一个根据个人习惯使用的.vimrc文件.我的有以下功能等,读者可以根据自己的 个人喜好去配置自己的vim. 1.自动插入文件头 ,新建C.C++源文件时自动插入表头:包括文件 ...

  9. 【软件笔记】 ◆笔记·I◆ 各类冷门函数细解

    [软件笔记·I] 各类冷门函数细解 ■题外话■ 总觉得作为一个志向远大的 coder (٩(◕‿◕。)۶),我觉得单单只会做题是不够的所以我开始尝试自己编写软件!初入道的我并不知道C++其实并不太适合 ...

  10. mysql中列属性

    mysql列属性包括:NULL .default.comment.primary key.unique key 一.NULL定义方式:NULL(默认) NOT NULL 空属性有2个值,mysql数据 ...