huffman编码概述:

Huffman编码是一种基于字符出现频率进行编码的贪心算法,由David A. Huffman于1952年提出。它通过构建最优二叉树来实现字符编码,保证高频字符使用短编码,低频字符使用长编码,从而达到数据压缩的目的。

问题实例

 算法核心思想:

Huffman编码采用贪心算法策略,每次选择频率最小的两个节点合并,逐步构建二叉树。这种局部最优选择最终导致全局最优解。

算法步骤:

初始化:为每个字符创建叶子结点

循环合并:重复选择频率最小的两个节点合并,直到只剩一个根节点

编码生成:从根节点到叶子结点的路径生成编码(左0右1)

代码实现

#include<stdio.h>

#include<string.h>

#include<malloc.h>



typedef struct Hnode//定义哈夫曼树节点

{

 int weight;//权重

 int lchild,rchild,parent;//左右孩子和父节点指针

 int value;//字符值

}HTNode,*HuffmanTree;



typedef char **HuffmanCode;//哈夫曼编码表

void Select(HTNode HT[],int l,int &s1, int &s2)//选出权值最小的节点,并且通过s1和s2传出其下标

{

 int i,min1=32767,min2=32767;

 for(i=1;i<=l;i++)

 {

  if(HT[i].weight<min1&&HT[i].parent==0)

  {

   s2=s1;

   min2=min1;

   min1=HT[i].weight;

   s1=i;

  }else if(HT[i].weight < min2 && HT[i].parent==0){

   min2 = HT[i].weight;

   s2=i;

  }

 }

}



void CreateHuffman_tree(HuffmanTree &HT, int n)//哈夫曼树的建立

{

 int s1,s2;

 if(n <=1)

 {

  return ;

 }

 int m = 2*n-1;//哈夫曼树节点总数

 HT= new HTNode[m+1];

 int i=0;

//初始化节点

 for(i=1;i<=m;i++)

 {

  HT[i].parent=0;

  HT[i].lchild=HT[i].rchild=0;

  //HT[i].weight=0;

 }

 printf("输出各节点权重(即weight):");

 for(i=1;i<=n;i++)

 {

  scanf("%d",&HT[i].weight);

 }

//构建哈夫曼树

 for(i=n+1;i<=m;i++)//记得带等号,否则哈夫曼编码从一位数开始

 {

  Select(HT,i-1,s1,s2);

  HT[s1].parent=HT[s2].parent=i;

  HT[i].lchild=s1;

  HT[i].rchild=s2;

  HT[i].weight=HT[s1].weight+HT[s2].weight;

 }

}

//生成哈夫曼编码

void Huffman_code(HuffmanTree HT,HuffmanCode &HC,int n)

{

 char *cd;

 HC =new char*[n+1];

 cd =new char [n];

 cd[n-1]='\0';

 for(int i=1;i<=n;i++)

 {

   int start=n-1;

   int c=i;

   int 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()

{

 HuffmanTree HT;

 HuffmanCode HC;

 int i,n; 

 printf("哈夫曼树节点个数:");

    scanf("%d",&n);

 printf("a b c d e f\n");

    CreateHuffman_tree(HT, n);/*建立哈夫曼树*/

    Huffman_code(HT,HC,n);/*哈夫曼树编码*/

    for(i=1;i<=n;i++)/*输出字符、权值及编码*/

       printf("编码是:%s\n",HC[i]);

    return 0;

}

关键算法分析

节点选择策略

void Select(HTNode HT[], int l, int &s1, int &s2) {
    int i, min1 = 32767, min2 = 32767;
    
    for(i = 1; i <= l; i++) {
        if(HT[i].weight < min1 && HT[i].parent == 0) {
            s2 = s1;
            min2 = min1;
            min1 = HT[i].weight;
            s1 = i;
        } else if(HT[i].weight < min2 && HT[i].parent == 0) {
            min2 = HT[i].weight;
            s2 = i;
        }
    }
}

算法特点:

遍历所有未处理的节点

维护最小和次小权重的节点

确保每次选择都是当前最优解

树构建过程

for(int i = n + 1; i <= m; i++) {

    Select(HT, i - 1, s1, s2);

    HT[s1].parent = HT[s2].parent = i;

    HT[i].lchild = s1;

    HT[i].rchild = s2;

    HT[i].weight = HT[s1].weight + HT[s2].weight;

}

构建逻辑:

选择两个最小权重的节点

创建新节点作为它们的父节点

新节点的权重为子节点权重之和

重复直到构建完整棵树

编码生成机制:

while(f != 0) {

    start--;

    if(HT[f].lchild == c) {

        cd[start] = '0';

    } else {

        cd[start] = '1';

    }

    c = f;

    f = HT[f].parent;

}

编码规则:

左路径编码为'0'

右路径编码为'1'

从叶子到根反向生成编码

实验结果与分析

运行示例

输入示例数据后,程序输出各字符的Huffman编码:

 算法复杂度分析

时间复杂度

构建哈夫曼树:O(n²) - 每次选择最小节点需要线性扫描

生成编码:O(n²) - 每个字符需要回溯到根节点

优化可能:使用优先队列可将复杂度降至O(n log n)

空间复杂度

哈夫曼树存储:O(n)

编码表存储:O(n × L),其中L为平均编码长度

Huffman编码不仅是算法学习的经典案例,更是连接理论计算机科学与实际应用的桥梁。掌握这一算法为进一步学习信息论、数据压缩和编码理论奠定了坚实基础。Huffman编码展现了贪心算法在解决优化问题时的强大能力。通过局部最优选择的简单策略,实现了全局最优的数据压缩效果,这正是算法设计的艺术所在。

更多推荐