C++实现Huffman编码解码算法:原理与代码详解

1. 概述

Huffman编码是一种常用的数据压缩算法,它利用字符出现频率的差异,对出现频率高的字符使用较短的编码,对出现频率低的字符使用较长的编码,从而达到压缩数据的效果。本文将详细介绍Huffman编码解码算法的原理和实现,并提供完整的C++代码示例。

2. Huffman编码原理

2.1 哈夫曼树

Huffman编码的核心是哈夫曼树。哈夫曼树是一种二叉树,每个节点代表一个字符,节点的权值代表该字符出现的频率。哈夫曼树的构建过程如下:

  1. 将所有字符按照权值从小到大排序,并将其作为叶子节点;
  2. 选择权值最小的两个节点,将其合并成一个新的节点,新的节点的权值等于两个节点的权值之和;
  3. 将新的节点加入到排序后的节点列表中,重复步骤2,直到所有节点都被合并成一个节点为止。

2.2 编码

哈夫曼树构建完成后,就可以根据哈夫曼树进行编码。编码的规则如下:

  1. 从根节点出发,到达每个叶子节点的路径可以表示为0和1的组合,其中0表示向左子树遍历,1表示向右子树遍历;
  2. 将每个叶子节点的路径用0和1的组合表示,就得到了该字符的编码。

2.3 解码

解码的过程就是根据编码反向推导出原始字符。具体步骤如下:

  1. 从哈夫曼树的根节点开始遍历,若当前编码位为'0',则向左子树遍历,否则向右子树遍历,直到遇到叶子节点为止;
  2. 将遍历路径上的字符按顺序拼接起来,得到对应的原始字符;
  3. 重复步骤1和2,直到将所有编码都解码为原始字符。

3. C++代码实现

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<map>
#include <fstream>
#include<iostream>
#define MAXN 50
using namespace std;
typedef char **HuffmanCode;
typedef struct{
        int weight;
        int parent, lchild, rchild;
}HTNode,*HuffmanTree;

HuffmanTree HT;
HuffmanCode HC;
int m,s1,s2,start,c,f;
char *cd;
int z[30];
int weight[MAXN] ;

void Select(HuffmanTree HT,int n,int &s1,int &s2)//Select函数
{
        int min1 =0x3f3f3f3f; 
        int min2 =0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
                if(HT[i].parent)       continue;//不是根节点
                if(HT[i].weight < min1)
                {
                        min1 = HT[i].weight;
                        s1 = i;
                        continue;
                }
                if(HT[i].weight < min2 && HT[i].weight >= min1)
                {
                        min2 = HT[i].weight;
                        s2 = i;
                }
        }
}

void CreatHuffmanTree(HuffmanTree &HT ,int n ) 
{
	 if ( n <= 1 ) return;
	 int m = 2 * n  ;
	 HT = new HTNode[m+1] ;
	 for (int a = 1 ; a <= m ; a ++ ) 
	 {
		 HT[a].parent = HT[a].lchild = HT[a].rchild = HT[a].weight = 0;
	 }
	 for (int j = 1 ; j <= n ; j ++ ) 
	 {
		 HT[j].weight = weight[j] ;
	 }
	 int s1 ,s2 ;
	 for (int i = n + 1 ; i <= m ; i ++ ) 
	 {
		 Select(HT ,i - 1 ,s1 ,s2 );
		 HT[s1].parent = i ; HT[s2].parent = i ;
		 HT[i].lchild = s1 ,HT[i].rchild = s2 ;
		 HT[i].weight = HT[s1].weight + HT[s2].weight ;
	 }
} //编码
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{
        HC = new char*[n+1];
        cd = new char[n];
        cd[n-1] = '\0';
        for(int i=1; i<=n; ++i)
        {
                start = n-1;
                c = i;
                f = HT[i].parent;
                while(f!=0)//
                {
                        --start;
                        if(HT[f].lchild == c)   cd[start]='0';
                        else                    cd[start]='1';
                        c = f;
                        f = HT[f].parent;//向上回溯
                }
                HC[i] = new char[n-start];
                strcpy(HC[i],&cd[start]);
        }
        delete cd;
}
//解码
int main()
{
//        freopen("in.txt", "r", stdin);//文件要和代码放在同一个文件夹
//        freopen("out.txt", "w", stdout);
        char s[MAXN];
        scanf("%s",s); 
        int len = strlen(s);
        memset(z,0,sizeof(z));
        for(int i=0;i<len;i++)//统计字符个数
                z[s[i]-'A'+1]++;
        int cnt=0;
        for(int j=1;j<=26;++j)
                if(z[j])
                        cnt++;
        for(int k=1;k<=26;k++)
                if(z[k])
                printf("%c:%d\n",'A'+k-1,z[k]);

        CreatHuffmanTree(HT,cnt);

        //输出一下看看是否建立好哈夫曼数
        printf("\n i   weight  parent  lchild  rchild \n");
        for(int x=1; x<=m; ++x)
                printf("%2d %5d %8d %8d %8d\n",x,HT[x].weight,HT[x].parent,HT[x].rchild,HT[x].lchild);


        CreatHuffmanCode(HT,HC,cnt);
        printf("\n\nHuffmanCode:\n");
        printf(" i  Char   Code\n\n");
        for(int t=1; t<=cnt; ++t)
                printf("%2d   %c     %s\n",t,t+'A'-1,HC[t]);

        puts("");
        for(int z=0;z<len;z++)
                printf("%s",HC[s[z]-'A'+1]);
        return 0;
}

string decode(HuffmanTree HT, string encoded) {
    int len = encoded.length();
    string decoded = "";
    int i = 0;
    while (i < len) { // 遍历编码串
        int p = m; // 从哈夫曼树的根节点开始
        while (HT[p].lchild != 0 || HT[p].rchild != 0) { // 遍历到叶子节点
            if (encoded[i] == '0') p = HT[p].lchild;
            else p = HT[p].rchild;
            i++;
        }
        decoded += char(p - m + 'A' - 1); // 将遍历路径上的字符按顺序拼接起来
    }
    return decoded;
}

4. 代码解释

4.1 CreatHuffmanTree 函数

void CreatHuffmanTree(HuffmanTree &HT ,int n ) 
{
	 if ( n <= 1 ) return;
	 int m = 2 * n  ;
	 HT = new HTNode[m+1] ;
	 for (int a = 1 ; a <= m ; a ++ ) 
	 {
		 HT[a].parent = HT[a].lchild = HT[a].rchild = HT[a].weight = 0;
	 }
	 for (int j = 1 ; j <= n ; j ++ ) 
	 {
		 HT[j].weight = weight[j] ;
	 }
	 int s1 ,s2 ;
	 for (int i = n + 1 ; i <= m ; i ++ ) 
	 {
		 Select(HT ,i - 1 ,s1 ,s2 );
		 HT[s1].parent = i ; HT[s2].parent = i ;
		 HT[i].lchild = s1 ,HT[i].rchild = s2 ;
		 HT[i].weight = HT[s1].weight + HT[s2].weight ;
	 }
} 

该函数用于构建哈夫曼树,它接收一个指向哈夫曼树的指针和字符个数作为参数。函数首先分配内存空间,然后将每个字符的权值存储到哈夫曼树节点的 weight 属性中。接着,函数调用 Select 函数选择权值最小的两个节点,并将它们合并成一个新的节点,重复该步骤直到所有节点都被合并成一个节点为止。

4.2 CreatHuffmanCode 函数

void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{
        HC = new char*[n+1];
        cd = new char[n];
        cd[n-1] = '\0';
        for(int i=1; i<=n; ++i)
        {
                start = n-1;
                c = i;
                f = HT[i].parent;
                while(f!=0)//
                {
                        --start;
                        if(HT[f].lchild == c)   cd[start]='0';
                        else                    cd[start]='1';
                        c = f;
                        f = HT[f].parent;//向上回溯
                }
                HC[i] = new char[n-start];
                strcpy(HC[i],&cd[start]);
        }
        delete cd;
}

该函数用于生成哈夫曼编码,它接收一个指向哈夫曼树的指针、一个指向哈夫曼编码的指针和字符个数作为参数。函数首先为哈夫曼编码分配内存空间,然后遍历哈夫曼树,从每个叶子节点向上回溯到根节点,将路径上的0和1存储到 cd 数组中,最后将 cd 数组中的编码复制到哈夫曼编码数组中。

4.3 decode 函数

string decode(HuffmanTree HT, string encoded) {
    int len = encoded.length();
    string decoded = "";
    int i = 0;
    while (i < len) { // 遍历编码串
        int p = m; // 从哈夫曼树的根节点开始
        while (HT[p].lchild != 0 || HT[p].rchild != 0) { // 遍历到叶子节点
            if (encoded[i] == '0') p = HT[p].lchild;
            else p = HT[p].rchild;
            i++;
        }
        decoded += char(p - m + 'A' - 1); // 将遍历路径上的字符按顺序拼接起来
    }
    return decoded;
}

该函数用于解码哈夫曼编码,它接收一个指向哈夫曼树的指针和一个编码字符串作为参数。函数首先遍历编码字符串,从哈夫曼树的根节点开始,根据编码位的值选择左子树或右子树遍历,直到遍历到叶子节点为止。然后将叶子节点对应的字符添加到解码后的字符串中,重复该步骤直到遍历完整个编码字符串为止。

5. 总结

本文介绍了Huffman编码解码算法的原理和实现,并提供完整的C++代码示例。文章涵盖了哈夫曼树的构建、编码、解码等步骤,并对代码进行注释解释。希望本文能帮助读者更好地理解Huffman编码解码算法。

C++实现Huffman编码解码算法:原理与代码详解

原文地址: https://www.cveoy.top/t/topic/lPxD 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录