问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

哈夫曼编码(Huffman Coding)原理与实践

创作时间:
作者:
@小白创作中心

哈夫曼编码(Huffman Coding)原理与实践

引用
CSDN
1.
https://blog.csdn.net/2401_87074443/article/details/146139990

哈夫曼编码是一种广泛应用于数据压缩领域的编码技术,由David A. Huffman在1952年提出。它基于贪心算法,能够根据字符出现的频率生成最优的二进制编码,从而实现数据的高效压缩。本文将详细介绍哈夫曼编码的背景、理论基础以及具体的算法实现过程。

一、背景介绍

哈夫曼编码由David A. Huffman在 1952 年提出,被广泛应用于文件压缩(如 ZIP)、图像压缩(如 JPEG)、视频编码(如 H.264)等领域。1952 年,麻省理工学院(MIT)的一名研究生David A. Huffman在期末考试选择参加考试还是写一篇期末论文时,解决了信息编码最优化问题,提出了一种基于贪心算法的编码方法,即哈夫曼编码。(如何用最少的二进制数来表示信息)

二、信息论的基本概念和现代信息压缩技术的基本思想

我们在发送消息的时候我们都会将信息先利用二进制编码,再将最少的二进制数编码信息传递至接受的一方,通过解码来接受信息。

编码所需要满足的条件

  1. 每单个字符都必须映射到一个唯一的二进制编码
  2. 保证解码七能够进行唯一的解码(无损压缩)

无损压缩的选择

使用尽量少的比特数(二进制长度bits)进行压缩
衡量信息——信息量与不确定性有关,信息量与发生概率成反比

三、信息熵

信息熵(Entropy)是信息论中的一个核心概念,用来衡量信息的不确定性或平均信息量。它由克劳德·香农(Claude Shannon)在 1948 年的信息理论中提出,因此也称为香农熵(Shannon Entropy)

信息熵反映了一个消息源的平均信息量,如果某个系统的不确定性越大,则它包含的信息量也越大,信息熵也就越高。相反,如果某个系统的确定性越高(比如某个事件总是发生),那么它的信息熵就越低。

信息熵的数学公式

信息熵的直观理解

  1. 如果一个系统的所有状态都等可能发生(比如抛一枚公平的硬币),则信息熵最大,因为我们无法预先确定它的结果。
  2. 如果某个状态的概率非常高(比如天气预报说 99% 明天是晴天),那么信息熵会很低,因为我们几乎可以确定它的结果。
  3. 如果一个事件是确定的(概率是 1),那么信息熵为 0,因为没有不确定性,不需要额外的信息来描述它。

信息熵与哈夫曼编码的关系

在哈夫曼编码中,我们的目标是用最少的比特数来编码数据,也就是设计一个最优编码方案。信息熵在其中起到了指导作用:

  1. 信息熵是理论上的最优压缩极限
  • 任何编码的平均比特长度不可能小于信息熵,即: 平均编码长度≥H(X)\text{平均编码长度} \geq H(X)平均编码长度≥H(X)
  • 也就是说,信息熵是我们最理想的压缩目标,哈夫曼编码能够接近这个最优极限。
  1. 字符出现的概率影响编码长度
  • 在哈夫曼编码中,频率高的字符用短编码,频率低的字符用长编码,从而减少整体编码的平均长度。
  • 这正是利用了信息熵的性质——如果某个符号的概率高,它所包含的信息量少,我们应该用更少的比特来表示它。

四、哈夫曼树

哈夫曼树的构建过程如下:

  1. 统计字符频率:首先,计算每个字符在文本中出现的次数。
  2. 创建节点并构建最小堆:将每个字符视为一个单独的树节点,节点的权重为该字符的频率。然后,使用最小堆(优先队列)存储这些节点,使得频率最低的节点始终位于最前。
  3. 合并最小的两个节点:从最小堆中取出两个最小频率的节点,创建一个新的父节点,其权重等于两个子节点的权重之和。这个新节点没有字符值,它仅仅是两个子节点的父节点。
  4. 重复合并,直到只剩下一个根节点:重复此过程,直到所有节点合并成一棵完整的二叉树,即哈夫曼树。
  5. 生成哈夫曼编码:从根节点出发,规定左子树路径记为 0,右子树路径记为 1,这样每个叶子节点(字符)都有一个唯一的二进制编码。

哈夫曼树的特点:

  • 前缀无冲突:生成的编码不会有前缀重叠,确保解码时不会产生歧义。
  • 最优编码:在已知字符频率的情况下,哈夫曼树生成的编码是最短的。
  • 贪心算法应用:构建过程采用了贪心策略,每次合并最小的两个节点,保证了整体的最优解。

五、算法实践

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NODES 256  // 最大字符种类
#define MAX_TREE_NODES (MAX_NODES * 2 - 1)
typedef struct {
    char ch;
    int freq;
    char code[MAX_NODES];
} HuffmanNode;
typedef struct {
    int weight;
    int parent, left, right;
} TreeNode;
TreeNode huffmanTree[MAX_TREE_NODES]; // 哈夫曼树
HuffmanNode huffmanCode[MAX_NODES];   // 哈夫曼编码表
int totalNodes = 0;                   // 节点总数
// **1. 统计字符频率**
void calculateFrequency(const char *str, int freq[]) {
    while (*str) {
        freq[(unsigned char)(*str)]++;
        str++;
    }
}
// **2. 选择两个最小权重的节点**
void selectMin(int n, int *s1, int *s2) {
    int min1 = -1, min2 = -1;
    for (int i = 0; i < n; i++) {
        if (huffmanTree[i].parent == -1) {  // 仅选择尚未加入树的节点
            if (min1 == -1 || huffmanTree[i].weight < huffmanTree[min1].weight) {
                min2 = min1;
                min1 = i;
            } else if (min2 == -1 || huffmanTree[i].weight < huffmanTree[min2].weight) {
                min2 = i;
            }
        }
    }
    *s1 = min1;
    *s2 = min2;
}
// **3. 构建哈夫曼树**
void buildHuffmanTree(int freq[]) {
    int n = 0;
    
    // 初始化叶子节点
    for (int i = 0; i < MAX_NODES; i++) {
        if (freq[i] > 0) {
            huffmanTree[n].weight = freq[i];
            huffmanTree[n].parent = -1;
            huffmanTree[n].left = -1;
            huffmanTree[n].right = -1;
            huffmanCode[n].ch = i;
            huffmanCode[n].freq = freq[i];
            n++;
        }
    }
    
    totalNodes = n;
    
    // 创建非叶子节点
    for (int i = n; i < 2 * n - 1; i++) {
        int s1, s2;
        selectMin(i, &s1, &s2);
        
        huffmanTree[i].weight = huffmanTree[s1].weight + huffmanTree[s2].weight;
        huffmanTree[i].left = s1;
        huffmanTree[i].right = s2;
        huffmanTree[i].parent = -1;
        huffmanTree[s1].parent = i;
        huffmanTree[s2].parent = i;
    }
}
// **4. 生成哈夫曼编码**
void generateHuffmanCodes() {
    char tempCode[MAX_NODES];
    int index = 0;
    for (int i = 0; i < totalNodes; i++) {
        if (huffmanTree[i].left == -1 && huffmanTree[i].right == -1) {
            int current = i, parent;
            index = 0;
            while (huffmanTree[current].parent != -1) {
                parent = huffmanTree[current].parent;
                if (huffmanTree[parent].left == current) {
                    tempCode[index++] = '0';
                } else {
                    tempCode[index++] = '1';
                }
                current = parent;
            }
            tempCode[index] = '\0';
            // 反转编码
            for (int j = 0; j < index; j++) {
                huffmanCode[i].code[j] = tempCode[index - j - 1];
            }
            huffmanCode[i].code[index] = '\0';
        }
    }
}
// **5. 打印哈夫曼编码表**
void printHuffmanCodes() {
    printf("\nHuffman Codes:\n");
    for (int i = 0; i < totalNodes; i++) {
        if (huffmanTree[i].left == -1 && huffmanTree[i].right == -1) {
            printf("Character: %c | Frequency: %d | Code: %s\n",
                   huffmanCode[i].ch, huffmanCode[i].freq, huffmanCode[i].code);
        }
    }
}
// **6. 编码输入字符串**
void encodeString(const char *str, char *encodedStr) {
    encodedStr[0] = '\0';
    while (*str) {
        for (int i = 0; i < totalNodes; i++) {
            if (huffmanTree[i].left == -1 && huffmanTree[i].right == -1 && huffmanCode[i].ch == *str) {
                strcat(encodedStr, huffmanCode[i].code);
                break;
            }
        }
        str++;
    }
}
// **7. 解码哈夫曼编码**
void decodeString(const char *encodedStr, char *decodedStr) {
    int index = 0;
    int current = totalNodes - 1;
    
    while (*encodedStr) {
        if (*encodedStr == '0') {
            current = huffmanTree[current].left;
        } else {
            current = huffmanTree[current].right;
        }
        
        if (huffmanTree[current].left == -1 && huffmanTree[current].right == -1) {
            decodedStr[index++] = huffmanCode[current].ch;
            current = totalNodes - 1;
        }
        
        encodedStr++;
    }
    decodedStr[index] = '\0';
}
// **8. 主函数**
int main() {
    char input[] = "hello huffman";
    int freq[MAX_NODES] = {0};
    calculateFrequency(input, freq);
    buildHuffmanTree(freq);
    generateHuffmanCodes();
    printHuffmanCodes();
    // 编码字符串
    char encodedStr[1024];
    encodeString(input, encodedStr);
    printf("\nEncoded String: %s\n", encodedStr);
    // 解码字符串
    char decodedStr[1024];
    decodeString(encodedStr, decodedStr);
    printf("\nDecoded String: %s\n", decodedStr);
    return 0;
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号