哈夫曼树以及哈夫曼编码

哈夫曼树又叫最优二叉树,即叶子结点带权路径长度之和(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;
}

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值