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. Windows Host 文件

    Windows XP Home / Windows 7/ Windows Server 2008 c:\windows\system32\drivers\etc\hosts 如果碰到Localhost ...

  2. 腾讯云、CloudXNS域名服务器

    腾讯云 f1g1ns1.dnspod.net f1g1ns2.dnspod.net CloudXNS lv3ns1.ffdns.net lv3ns2.ffdns.net lv3ns3.ffdns.ne ...

  3. mysql轮廓总结

    架构=数据类型.索引.分片.主从复制原理.数据备份 学习软件,都应该先从架构入手,每一层掌握就行.mysql难吗?从其架构层开始,就不难啦. 架构结构:http://www.cnblogs.com/h ...

  4. 2017.11.9 如何利用JS做登陆验证界面

    ()案例----JavaScript实现输入验证 需要验证的表单输入域和要求 用户名不能为空,是否符合规定的格式 密码长度是否超过6,两次密码输入一致 邮箱地址:邮箱地址必须符合邮箱形式 ~~~注意提 ...

  5. Java 压缩文件夹工具类(包含解压)

    依赖jar <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons ...

  6. HttpServerUtility 和 HttpUyility

    参考:msdn HttpServerUtility 提供用于处理 Web 请求的 Helper 方法. 2017/08/07            加密解码 这个类没有构造函数,所以不能直接new. ...

  7. js字符串内容包含单引号‘’和双引号“”怎么办?

    如果javascript中的字符串包含单引号和双引号,可以用转义字符来标识 'I\'m \"OK\"!'; 表示的字符串内容是:I'm "OK"! 转义字符\可 ...

  8. 前端jQuery基本语法

    1.概念 1.1基础知识 jQuery是一个兼容多浏览器的JavaScript库,封装了开发过程中常用的一些功能,类似Python模块 jQuery就是用JS写的,JS是基础 jQuery写起来简单, ...

  9. ReactiveCocoa实战: 模仿 "花瓣",重写 LeanCloud Rest Api的iOS REST Client.

    这一次我们将要讨论的是移动开发中比较重要的一环--网络请求的封装.鉴于个人经验有限,本文将在一定程度上参考 基于AFNetworking2.0和ReactiveCocoa2.1的iOS REST Cl ...

  10. 表单验证实现React-router跳转

    方法一:broserHistory.push handleSubmit(e){ e.preventDefault(); const path = '/demo'; broserHistory.push ...