哈夫曼编码(字符串压缩)

本文介绍了在面试中遇到的字符串压缩问题,从最初的十进制编码到利用哈夫曼编码进行优化。哈夫曼编码是一种变长编码方法,通过对字符出现概率的统计,使得高概率字符使用较短编码,从而节省存储空间。文章详细阐述了哈夫曼编码的构建过程,包括按概率排序、合并节点、分配01标记以及如何根据二叉树生成编码,以实现无损压缩。
摘要由CSDN通过智能技术生成

之前去面试,被面试官问到这么一个问题:

假如现在有一个设备,仅允许输入a~z以及空格这27个字符,请问,如何编码保存,能使其占用的内存空间最小?

刚开始答的时候,想到了用asc编码的形式,将27个字符分别用27位十进制数字进行保存,之后再将这27个十进制数字转为二进制存储到硬盘中,其占用的大小小于 25 字节(00000~11111),之后面试官又问:

如果当前字符串是连续的多个字符组成的,如何优化存储方式?

想了一会儿还差一点,面试官提示说可以进行标记,然后跟我讲,用剩下的五位(11100~11111)存储特殊字符(比如存储一个“#”)对每个字符进行标记存储,例如 #a#50# 就表示连续的五十个a #abc#100# 就表示连续的一百个 abc 用这种方式就可以做到连续的字符用定长存储,最后面试官又问:

如果这一串字符中,有的字符出现的概率十分大,有的字符出现的概率很小,怎么去优化存储空间呢?

这里答得不是很好,具体用到的数据结构应该是哈夫曼编码,所以在这复习一下哈夫曼编码。

哈夫曼编码(Huffman Coding)是一种编码方法,它使用变长的编码表对源符号进行编码,其中变长编码表是通过一种对字符出现几率统计的方法得到的,出现几率高的字符使用较短的编码,反之采用较长的编码,由此可以使得编码之后的字

在C语言中,实现这个功能需要几个步骤: 1. **统计字符频率**: 首先,你需要读取用户输入的英文字符串,然后遍历每个字符,使用哈希表(如结构体数组或关联数组)存储每个字符及其出现的次数。同时计算总的字符数,这将作为概率的依据。 ```c #include <stdio.h> #include <string.h> typedef struct { char ch; int count; } CharCount; void count_chars(char* str, CharCount* counts) { for (int i = 0; str[i] != '\0'; i++) { if (isalpha(str[i])) { // 只统计字母 counts[str[i]-'a'].count++; // 'a' 到 'z' 的 ASCII 码范围 } } } ``` 2. **构建Huffman树并生成编码**: 根据字符频率构建一个最小带权路径长度二叉树(Huffman Tree),然后生成每个字符的编码。这里可以利用优先队列数据结构。 ```c struct Node { char ch; int freq; struct Node *left, *right; }; // ... 编写插入节点、合并等Huffman树操作 ... void build_huffman(CharCount* counts, struct Node** root) { // ... 实现二叉树构建过程 ... } char* get_encoding(struct Node* node) { // ... 实现递归生成编码函数 ... } ``` 3. **压缩和解码**: 对原始字符串进行编码,通过替换字符为其对应的Huffman编码。压缩后的数据可以存储在一个缓冲区中。解码时,从缓冲区读取数据,按照编码规则重建字符串。 ```c void compress_decode(char* input, char* encoded, CharCount* counts, struct Node* huff_tree) { int encoded_len = strlen(encoded); char decoded[input_length]; // 假设已知原始字符串长度 for (int i = 0; i < encoded_len; i++) { decoded[i] = decode(encoded[i], huff_tree); } printf("Decoded string: %s\n", decoded); } ``` 4. **完整程序示例**: 将上述部分组合在一起,创建一个完整的主函数,包括用户输入、处理以及显示结果。 ```c int main() { // ... 初始化变量 ... char user_input[100]; CharCount counts[26]; struct Node* root = NULL; // 用户输入阶段 printf("Enter a string: "); fgets(user_input, sizeof(user_input), stdin); // 计算字符频率 count_chars(user_input, counts); // 构建Huffman树 build_huffman(counts, &root); // 编码和解码 char encoded_encoded[strlen(user_input)]; // 原始长度用于分配内存 compress_decode(user_input, encoded_encoded, counts, root); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值