Huffman(哈夫曼)编码的C语言实现

Huffman(哈夫曼)编码的C语言实现

本文将给出C语言的Huffman编码的原理,示例及C语言仿真结果,代码。

一、Huffman编码原理及举例

Huffman编码是一种信源编码,其编码目的在于以最高的编码效率利用信道容量。
例如,假定消息有五种字符序列构成,各字符出现的概率是给定的,设为a,b,c,d,e。出现概率为0.12,0.40,0.15,0.08,0.25。下面给出两种编码(映射):

字符 符号概率 编码方式1 编码方式2
a 0.12 000 000
b 0.40 001 11
c 0.15 010 01
d 0.08 011 001
e 0.25 100 10

编码方式1中任意3位二进制数字串都不是另一个3位二进制数字串的前缀,故其有前缀性毋庸置疑。在译码时,每次取3位二进制数字串,每3位译码为1个字符。
编码方式2其实也具有前缀性。但由于其编码的比特长度不同,难以看出其是否具有前缀性。我们不妨用二叉树来表示其编码(前缀编码均可以用二叉树表示):
编码方式1的二叉树

二叉树——编码方式1

编码方式2的二叉树

二叉树——编码方式2

由此,可以将前缀编码看作二叉树中的路径。每个结点的左分支附0,右分支附1。将字符作为叶结点的标号。从根结点到叶结点的路径上遇到的0或1构成的序列就是对应叶结点字符的编码。
对于编码方式1,所有字符编码长度为3,则其平均编码长度为3。但编码方式2的平均编码长度为2.2。显然编码方式2的效率更高。

采用Huffman算法可以得到最优前缀编码。

首先,从给定的字符集里选取两个出现概率最小的两个字符,以之前的例子为字符a,d。构造一个父结点,符号设为x,其对应概率为a,d符号概率之和,他的子结点分别为a,d。然后对其余结点和新结点组成的字符集和概率集按同样方式递归得到前缀编码二叉树。遍历二叉树即可得到最优前缀编码。

其构造的顺序如下:
构造顺序通过以上方式,可以得到最优的编码方式:

字符 符号概率 Huffman编码
a 0.12 1111
b 0.40 0
c 0.15 110
d 0.08 1110
e 0.25 10

经过计算可知,其平均编码长度为2.15。

二、Huffman编码的C语言实现

符号分别为A~P,共16个符号,其出现概率如下:

符号 概率
A 0.06
B 0.12
C 0.15
D 0.05
E 0.06
F 0.02
G 0.07
H 0.03
I 0.13
J 0.09
K 0.07
L 0.06
M 0.02
N 0.02
O 0.01
P 0.04

1、初始化

首先输入初始信息并设置结点树,这里不使用指针变量,而是以索引为地址。

struct Huffman
{
   
	double weight;
	int lchild;
	int rchild;
	int parent;
};	//Huffman tree结构体定义

结构体表示一个二叉树结点,设置的结点数量应该为node_num个,直接定义为H。
首先对二叉树初始化。对二叉树的大小(结点数量)应为符号数的2倍减1。为修改方便,对结点数,符号数,概率空间作宏定义:

#define symbol_num 16
#define node_num 2 * (symbol_num) - 1
double symbol_P[symbol_num] = {
    0.06, 0.12, 0.15, 0.05, 0.06, 0.02, \
0.07, 0.03, 0.13, 0.09, 0.07, 0.06, 0.02, 0.02, 0.01, 0.04};

初始化前symbol_node(16)个结点的权值weight为对应的概率,为后续排序方便,设置其余结点权值为1。其余子结点,父结点编号参照数据结构设置为 -1。为后续提取符号序号,构造list数组存储排序后的序号。为避免破坏原概率空间,可以设置新的初始概率空间以供更改。

for (int i = 0; i < node_num; i++)
{
   
	if (i < symbol_num)
		symbol_Ptemp[i] = symbol_P[i];
	else
		symbol_Ptemp[i] = 1;
}

for (int i = 0; i < node_num; i++)
{
   
	list[i] = i;
}
for (int i = 0; i < node_num; i++)
{
   
	H[i].parent = -1;
	H[i].lchild = -1
  • 13
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼编码是一种无损压缩算法,它的核心是构造一棵哈夫曼树,然后利用哈夫曼树进行编码和解码。贪心策略是哈夫曼编码的基础,它的思想是将出现频率较高的字符用较短的编码表示,出现频率较低的字符用较长的编码表示,以达到压缩数据的目的。 以下是哈夫曼编码C语言实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NODES 256 typedef struct node { unsigned char ch; int freq; struct node *left, *right; } Node; typedef struct heap { int size; Node *nodes[MAX_NODES]; } Heap; Node *new_node(unsigned char ch, int freq) { Node *node = (Node *) malloc(sizeof(Node)); node->ch = ch; node->freq = freq; node->left = NULL; node->right = NULL; return node; } Heap *new_heap() { Heap *heap = (Heap *) malloc(sizeof(Heap)); heap->size = 0; return heap; } void swap(Node **a, Node **b) { Node *temp = *a; *a = *b; *b = temp; } void heapify(Heap *heap, int i) { int smallest = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < heap->size && heap->nodes[left]->freq < heap->nodes[smallest]->freq) { smallest = left; } if (right < heap->size && heap->nodes[right]->freq < heap->nodes[smallest]->freq) { smallest = right; } if (smallest != i) { swap(&heap->nodes[i], &heap->nodes[smallest]); heapify(heap, smallest); } } void insert(Heap *heap, Node *node) { heap->nodes[heap->size++] = node; int i = heap->size - 1; while (i > 0 && heap->nodes[(i - 1) / 2]->freq > heap->nodes[i]->freq) { swap(&heap->nodes[(i - 1) / 2], &heap->nodes[i]); i = (i - 1) / 2; } } Node *extract_min(Heap *heap) { Node *node = heap->nodes[0]; heap->nodes[0] = heap->nodes[heap->size - 1]; heap->size--; heapify(heap, 0); return node; } Node *build_huffman_tree(int *freq) { Heap *heap = new_heap(); for (int i = 0; i < 256; i++) { if (freq[i] > 0) { insert(heap, new_node(i, freq[i])); } } while (heap->size > 1) { Node *left = extract_min(heap); Node *right = extract_min(heap); Node *parent = new_node(0, left->freq + right->freq); parent->left = left; parent->right = right; insert(heap, parent); } Node *root = extract_min(heap); free(heap); return root; } void encode(Node *root, int *code, int len, char **codes) { if (root->left == NULL && root->right == NULL) { code[len] = '\0'; codes[root->ch] = (char *) malloc(sizeof(char) * (len + 1)); strcpy(codes[root->ch], (char *) code); } else { code[len] = '0'; encode(root->left, code, len + 1, codes); code[len] = '1'; encode(root->right, code, len + 1, codes); } } void print_codes(char **codes) { for (int i = 0; i < 256; i++) { if (codes[i] != NULL) { printf("%c: %s\n", i, codes[i]); } } } void destroy_tree(Node *root) { if (root != NULL) { destroy_tree(root->left); destroy_tree(root->right); free(root); } } void destroy_codes(char **codes) { for (int i = 0; i < 256; i++) { if (codes[i] != NULL) { free(codes[i]); } } free(codes); } int main() { int freq[256] = {0}; char c; while ((c = getchar()) != EOF) { freq[c]++; } Node *root = build_huffman_tree(freq); int code[256]; char **codes = (char **) malloc(sizeof(char *) * 256); encode(root, code, 0, codes); print_codes(codes); destroy_tree(root); destroy_codes(codes); return 0; } ``` 该实现中,首先定义了一个`Node`结构体用于表示哈夫曼树中的节点,包括字符、出现频率以及左右子节点。`Heap`结构体用于表示最小堆,包括堆的大小和节点数组。`new_node`函数用于新建一个节点,`new_heap`函数用于新建一个最小堆。`swap`函数用于交换两个节点,`heapify`函数用于从指定位置开始调整最小堆,`insert`函数用于插入一个新节点到最小堆中,`extract_min`函数用于取出最小节点,`build_huffman_tree`函数用于构建哈夫曼树。 当哈夫曼树构建完成后,调用`encode`函数对每个字符进行编码,`print_codes`函数用于输出每个字符的编码,`destroy_tree`函数用于销毁哈夫曼树,`destroy_codes`函数用于销毁编码数组,释放动态分配的内存。 以上就是贪心策略哈夫曼编码C语言实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值