【数据结构与算法】二叉树——哈夫曼编码

最近有很多的小朋友问我什么是哈夫曼编码,哈夫曼编码是一种可变字长的编码,那什么是可变字长呢?就是一句话里的每一个字符(ASCII码)它的位数(长度)是不一样的。就像我们一句话(AAAACCCCCDDDDBBE)有A,B,C,D,E五种字符,在这里我们可以用01表示A字符,用001表示B字符,用11表示C字符,用10表示D字符,用000表示E字符。如下图:

既然知道了哈夫曼编码是什么了,那又有好奇的小朋友又会问了:那么哈夫曼编码是按照什么原理生成的呢?

在这里我就要告诉大家,哈夫曼编码是根据哈夫曼树生成的,如果看到这里有小朋友不知道什么是树的话可以先去学习一下二叉树。哈夫曼树是一种特殊的二叉树,为什么特殊?因为哈夫曼树只有叶子节点保存了数据。

那接下来我们就来分析怎么去对一句话的每一个字符生成编码:

以前的编码方式就是根据一句话中不同字符的个数,来确定字符编码的长度,然后按照一定的顺序对字符进行唯一的编码。这样虽然简单,但也造成了数据存储空间的高占用。哈夫曼编码就是为了解决高占用的问题,从原来的基础上缩短一句话的长度,可能有数学好的小朋友们已经猜到了,一句话中,绝大多数情况下每个字符出现的概率是不一样的,竟然概率不一样,那么我们不就可以让出现概率高的字符更短点,而让出现概率低的字符更长点,不就可以让整句话的编码缩短吗?

知道了原理,实现起来就简单了,给出我的代码:

#include<iostream>
#include<vector>
#include<string>
#include<stack>
using namespace std;
struct TreeNode {//树节点的构造
	int val;
	char ch;
	struct TreeNode *left, *right;
	TreeNode(int v, char c) :val(v), ch(c), left(NULL), right(NULL) {}
};
struct Node {//链节点的构造
	TreeNode tree;//树节点
	struct Node *next;
	Node(int v,char c):tree(v,c),next(NULL) {}
};
Node* sort(Node *list,Node *item) {//链表排序
	Node *p = list, *r;
	if (p->tree.val > item->tree.val) {
		item->next = p;
		list = item;
	}
	else {
		r = p; p = p->next;
		while (p != NULL) {
			if (p->tree.val > item->tree.val) {
				r->next = item;
				item->next = p;
				break;
			}
			r = p; p = p->next;
		}
		r->next = item;
	}
	return list;
}
void code(TreeNode *t, string &cd) {//运用递归实现哈夫曼编码的生成
	if (t->left != NULL || t->right != NULL) {
		cd.push_back(‘0‘); code(t->left, cd);
		cd.pop_back();
		cd.push_back(‘1‘); code(t->right, cd);
		cd.pop_back();
	}
	else
		cout << t->ch << ‘ ‘ << cd << endl;
}
void getpower(string &str) {//获取权值并创建有序链表生成哈夫曼树
	vector<int>recode(129, 0);//129位的数组记录每个字符的出现次数
	Node *list = NULL, *p, *r=NULL;
	for (int i = 0; i < int(str.length()); i++)
		recode[str[i]]++;
	int n = 0;
	for (int i = 0; i < int(recode.size()); i++) {//对每一个链节点进行初始化
		if (recode[i]){
			if (list == NULL) {
				list = (Node*)malloc(sizeof(Node));
				list->tree.val = recode[i];
				list->tree.ch = i;
				list->tree.left = NULL;
				list->tree.right = NULL;
				list->next = NULL;
			}
			else {
				p = (Node*)malloc(sizeof(Node(recode[i], i)));
				p->tree.val = recode[i];
				p->tree.ch = i;
				p->tree.left = NULL;
				p->tree.right = NULL;
				p->next = NULL;
				list = sort(list, p);//排序
			}
			n++;
		}
	}
	p = list;
	while (p != NULL) {//输出检验
		cout << p->tree.ch << ":" << p->tree.val << endl;
		p = p->next;
	}
	while (n != 1) {
		p = list->next;
		r = (Node*)malloc(sizeof(Node));
		r->next = NULL;
		r->tree.val = list->tree.val + p->tree.val;
		r->tree.ch = 0;
		r->tree.left = &list->tree;
		r->tree.right = &p->tree;
		list = p->next;
		if(list!=NULL)
			list = sort(list, r);
		n--;
	}
	string s;
	cout << "\n生成编码:\n";
	code(&r->tree,s);
}
int main() {
	string str="Create HaffmanCode!";
	getpower(str);
	return 0;
}

生成如下哈夫曼树:

在这里我要说一下,一句话的哈夫曼编码是不唯一的,因为有的字符概率一样。而且有的哈夫曼编码的长度甚至还不一样,这就是另外一个问题了。

最后,聪明的小朋友可能还发现了哈夫曼编码的另一好处就是任何一个字符的编码都不会是其他字符的前缀,这就确保了即使传输的过程中没有空格,依然能够解码出相应的内容,这也是哈夫曼编码受到广泛应用的原因。

最后是个人设计过程中的总结:虽然代码看起来很多,但是只要思路清晰,而且用对了数据结构就很容易实现你想要的操作。以上代码是我在原来的基础上优化后的结果,本来还是想使用结构体的构造函数,还有在输出编码时使用迭代法实现。但都没有完成。等以后回头再看看。

原文地址:https://www.cnblogs.com/1000sakura/p/10015542.html

时间: 2024-11-06 23:24:57

【数据结构与算法】二叉树——哈夫曼编码的相关文章

贪心算法应用-哈夫曼编码

哈夫曼编码应用于数据文件和图像压缩的编码方式.其压缩率通常在20%~90%之间.在进行远距离通信时,通常需要把将要传送的文字转换为由二进制字符组成的字符串,并使要传送的电文总长度尽可能的短.显然只要将点文章出现次数多的字符采用尽可能短的编码,就可以减少要传送的电文总长度. 哈夫曼编码的核心思想: (1)每一个字符用一个0,1串作为其代码,并要求任意一个字符的代码都不是其他字符代码的前缀. (2)用字符在文件中出现的频率来建立一个用0,1串表示各字符的最优表示方式,即使出现频率高的字符获得较短编码

算法学习-哈夫曼编码(c++实现)

哈夫曼编码 哈夫曼编码虽然简单,但是是一个非常重要的编码方式,它解决了数据存储的一个非常重要的问题:压缩!它的编码方式是最优的,无损的,尤其在图像中使用的非常多.下面讲下它的原理. 编码方式 哈夫曼编码的构造是依据权值的大小来实现的.首先根据权值构造哈夫曼树,然后对哈夫曼树进行逆向遍历,从而找到每个节点的编码方式. 例如: abbcccdddde这个是一个字符串,一共有5个字符.每个字符的权值就是出现的频率,那么a就是1,b权值为2,c的权值为3,d的权值为4,e的权值为1.在普通的编码方式中,

数据结构(java语言描述)哈夫曼编码

原理:哈夫曼编码是根据将已给出的权值作为叶子结点,生成一颗哈夫曼树,然后使得权重最小. 首先生成已给权重的所有的叶子结点,然后取所有节点中最小和次小的结点作为左右孩子生成一个哈夫曼树,计算出父节点的权重放入给出的权重森林中,并把之前的最小和次小的结点从森林中删除,再在种种森林中找最小和次小的结点生成权重树....直到最终只剩下一个树为止. 哈夫曼树的结点用如下结点表示: (有权重,左右孩子,父节点,然后设置一个标识符标志结点是否已经放入哈夫曼树) package tree;/**********

一步两步学算法之哈夫曼编码(最优二叉树)

比较难理解的 都打了备注了 1 #include "stdio.h" 2 #include "stdlib.h" 3 #include "string.h" 4 char alphabet[]={'A','B','C','D'}; 5 typedef struct 6 { 7 int weight; //权值 8 int parent; //父节点序号 9 int left ; 10 int right; 11 }HuffmanTree; 12

【数据结构】树与树的表示、二叉树存储结构及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树与哈夫曼编码、集合及其运算

1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找--方法一:顺序查找(时间复杂度O(n)) int SequentialSearch(St

SDUT 3345 数据结构实验之二叉树六:哈夫曼编码

数据结构实验之二叉树六:哈夫曼编码 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 字符的编码方式有多种,除了大家熟悉的ASCII编码,哈夫曼编码(Huffman Coding)也是一种编码方式,它是可变字长编码.该方法完全依据字符出现概率来构造出平均长度最短的编码,称之为最优编码.哈夫曼编码常被用于数据文件压缩中,其压缩率通常在20%-90%之间.你的任务是对从键盘输入的一个字符串求出它的ASCII编码长度和哈夫曼

贪心算法-霍夫曼编码

霍夫曼编码是一种无损数据压缩算法.在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度.期望值降低,从而达到无损压缩数据的目的.例如,在英文中,e的出现机率最高,而z的出现概率则最低.当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26).用普通的表示方法时,每个

[数据结构] AVL树和AVL旋转、哈夫曼树和哈夫曼编码

1. AVL树 AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多次树旋转来重新平衡这个树. 节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反).带有平衡因子1.0或 -1的节点被认为是平衡的.带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树.平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来. 1.2AVL旋转 AVL树的基本操作一

数据结构:哈夫曼编码(php版)

演示网址:http://huffman.sinaapp.com/ 源文件下载地址:http://xiaocao.u.qiniudn.com/work/huffman-2013-12-19.zip 概述下: 哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩. 在计算机信息处理中,"哈夫曼编码"是一种一致性编码法(又称"熵编码法"),用于数据的无损耗压缩.     简单的,就是靠权值排序,然后,转码,最优保存. 实现功能: 保存译码:在服务器端保存源