哈夫曼树以及哈夫曼编码

来源:互联网 发布:php 修改$_get 编辑:程序博客网 时间:2024/05/13 08:52

哈夫曼树又叫最优二叉树,即叶子结点带权路径长度之和(WPL)最小。
关于哈夫曼树有在以下链接中的作者解释的很详细,不太清楚的同学可以去看一下。
https://www.cnblogs.com/zhangming-blog/p/5395950.html
下面我想补充下下哈夫曼编码,哈夫曼编码又称霍夫曼编码,或者最优编码。哈夫曼编码主要有以下特点:
一 : 不等长编码;
二: 最优编码;
三: 无歧义编码,即任意字符的编码都不是另一字符的前缀;
四: 所有应编码的字符位于叶子结点。
哈夫曼编码采用的是二进制编码方式,父结点与左儿子结点之间的路径编为0,与右儿子结点之间的路径编为一。举个例子来说明一下。
这里写图片描述
在这个例子中,1,2,3,4为待编码的字符.
1的编码序列为: 110
2的编码序列为: 111
3的编码序列为: 10
4的编码序列为: 0
可以看出,各字符的编码序列不尽相同,权重越大编码序列越短,即路径越短。
WPL = 1*3 + 2*3 + 3*2 + 4*1 = 19
需要注意的是:哈夫曼树一定是最优编码,但最优编码不一定需要通过哈夫曼树实现。这一点哈夫曼先生在他的论文也有说明,有兴趣的同学可以验证下。
下面是哈夫曼树的结构:

struct Node{    int weight;  //权重    int *code;   //编码    int codeLen;     //编码长度    Huffman left;  //左孩子结点    Huffman right;  //右孩子结点};

以下哈夫曼树的创建代码:

Huffman Creat_Huffman(int N){    int i, n;    MinHeap H;    Huffman T;    H = Creat_MinHeap(N);  //创建最小堆    n = H->size;      for( i = 1; i < n; i++ )    {        T = (Huffman)malloc( sizeof( struct Node ) ); //创建一个根结点        T->left = Delete(H);  //从最小堆中取权重最小的结点赋给左孩子结点        T->right = Delete(H); //同上        T->weight = T->left->weight + T->right->weight; //父节点的权重等于左右孩子结点权重之和        Insert( H, T );  //将父节点放入最小堆中    }    T = Delete(H);  // T指向堆中的头结点也就是哈夫曼树的根结点    return T;}

上面的代码中哈夫曼树的创建借助了最小堆来实现,对最小堆不太清楚的同学可以去看我的另一篇关于最小堆的博客。
此为链接:最小堆
下面是哈夫曼编码的代码实现:

void HuffmanCode( Huffman T, int len ){    static int s[20]; //静态数组,存储数据编码    if( T )    {        if( !T->left && !T->right )        {            printf("结点权重为 %-3d 的编码是: ", T->weight);            T->code = (int *)malloc( len * sizeof( int ) ); //开辟空间存储叶子结点编码            for( int i = 0; i < len; i++)            {                T->code[i] = s[i];  //将字符的编码存入对应结点中                printf("%d", T->code[i]);            }            T->codeLen = len;   //存储编码长度            printf("\n");        }        else        {           //递归遍历各结点的路径            s[len] = 0;            HuffmanCode( T->left, len+1 );            s[len] = 1;            HuffmanCode( T->right, len+1 );        }    }}

以下为全代码实现:

#include<stdio.h>#include<malloc.h>#define ElementType Huffman#define MaxSize 100  //堆堆的最大空间typedef struct Node* Huffman;typedef struct Heap* MinHeap;struct Node{    int weight;  //权重    int *code;   //编码    int codeLen;     //编码长度    Huffman left;  //左孩子结点    Huffman right;  //右孩子结点};struct Heap{    ElementType *element;  //存储元素    int size;  //元素的数量    int capacity; //堆的最大空间};//插入一个元素void Insert(MinHeap H, Huffman e){    int i;    if( H->size == H->capacity )   //判断堆是否已满    {        printf("堆已满!");        return ;    }    i = ++H->size;  //所存元素数量加1    for( ; i>=2 && H->element[i/2]->weight > e->weight; i /= 2)  //当要插入的元素小于父节点时,父节点向下挪动,要插入的元素继续向上比较        H->element[i] = H->element[i/2];    H->element[i] = e;}//创建最小堆MinHeap Creat_MinHeap(int n){    ElementType tmp;    int  i;    MinHeap H = (MinHeap)malloc( sizeof(struct Heap) );    H->element = (ElementType*)malloc( (MaxSize+1) * sizeof(ElementType) );    H->size = 0;    H->capacity = MaxSize;    for( i = 1; i <= n; i++ )     //将元素按输入顺序放入数组中    {        tmp = (ElementType)malloc( sizeof( struct Node ) );        tmp->left = NULL;        tmp->right = NULL;        scanf("%d", &tmp->weight);        Insert( H, tmp );    }    return H;}//删除根结点并返回其值ElementType Delete(MinHeap H){    int parent, child;    ElementType tmp, e;    if( H->size == 0)    {        printf("堆为空!");        return 0;    }    e = H->element[1];    tmp = H->element[H->size--];    for( parent = 1; parent*2 <= H->size; parent = child )    {        child = parent * 2;        if(child != H->size && H->element[child+1]->weight < H->element[child]->weight)            child++;          //child指向左右儿子中较小的那个;        if( tmp->weight <= H->element[child]->weight )   //如果tmp小于等于左右儿子中较小的那个,结束循环            break;        else                    //否则,较小的儿子上移,tmp进入下一层            H->element[parent] = H->element[child];    }    H->element[parent] = tmp;    return e;}//先序遍历输出哈夫曼树void Print( Huffman T ){    if( T )    {        printf("%d ", T->weight);        Print( T->left );        Print( T->right );    }}Huffman Creat_Huffman(int N){    int i, n;    MinHeap H;    Huffman T;    H = Creat_MinHeap(N);  //创建最小堆    n = H->size;    for( i = 1; i < n; i++ )    {        T = (Huffman)malloc( sizeof( struct Node ) );        T->left = Delete(H);        T->right = Delete(H);        T->weight = T->left->weight + T->right->weight;        Insert( H, T );    }    T = Delete(H);    return T;}//哈夫曼编码void HuffmanCode( Huffman T, int len ){    static int s[20]; //静态数组,存储数据编码    if( T )    {        if( !T->left && !T->right )        {            printf("结点权重为 %-3d 的编码是: ", T->weight);            T->code = (int *)malloc( len * sizeof( int ) ); //开辟空间存储叶子结点编码            for( int i = 0; i < len; i++)            {                T->code[i] = s[i];  //将字符的编码存入对应结点中                printf("%d", T->code[i]);            }            T->codeLen = len;   //存储编码长度            printf("\n");        }        else        {           //递归遍历各结点的路径            s[len] = 0;            HuffmanCode( T->left, len+1 );            s[len] = 1;            HuffmanCode( T->right, len+1 );        }    }}//计算带权最优编码长度int WPL( Huffman T ){    if( !T->right && !T->left)    {        return T->weight*T->codeLen;    }    else        return ( WPL( T->left ) + WPL( T->right ) );}int main(){    int N;    Huffman T;    scanf("%d", &N);  //需要编码的字符数量    T = Creat_Huffman(N);  //创建哈夫曼树    printf("\n哈夫曼树先序遍历结果为: ");    Print( T );   //先序遍历哈夫曼树    printf("\n");     HuffmanCode( T, 0 );   //对字符进行编码    printf("最优编码长度为: %d\n", WPL( T ));  //输出带权路径之和    return 0;}

以上即为所有代码,不懂之处,欢迎交流,不足之处,恳请指正。

原创粉丝点击