霍夫曼编码

1. 问题分析:设需要编码的字符集为{ d1 , d2 , ⋯ , dn } , 它们出现的频率为{ w1 , w2 , ⋯ , wn } , 应用霍夫曼树构造最短的不等长编码方案。

2. 算法实现

#include<bits/stdc++.h>

using namespace std;

typedef struct

{

    unsigned int weight;  //用来存放各个结点的权值

    unsigned int parent,LChild,RChild;  //指向双亲、孩子结点的指针

} HTNode, *HuffmanTree;  //动态分配数组,存储哈夫曼树

typedef char *HuffmanCode;  //动态分配数组,存储哈夫曼编码

//选择两个parent为0,且weight最小的结点s1和s2

void Select(HuffmanTree *ht,int n,int *s1,int *s2)

{

    int i,min;

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

    {

        if((*ht)[i].parent==0)

        {

            min=i;

            break;

        }

    }

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

    {

        if((*ht)[i].parent==0)

        {

            if((*ht)[i].weight<(*ht)[min].weight)

                min=i;

        }

    }

    *s1=min;

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

    {

        if((*ht)[i].parent==0 && i!=(*s1))

        {

            min=i;

            break;

        }

    }

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

    {

        if((*ht)[i].parent==0 && i!=(*s1))

        {

            if((*ht)[i].weight<(*ht)[min].weight)

                min=i;

        }

    }

    *s2=min;

}

//构造哈夫曼树ht,w存放已知的n个权值

void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)

{

    int m,i,s1,s2;

    m=2*n-1;    //总共的结点数

    *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));

    for(i=1; i<=n; i++)  //1--n号存放叶子结点,初始化

    {

        (*ht)[i].weight=w[i];

        (*ht)[i].LChild=0;

        (*ht)[i].parent=0;

        (*ht)[i].RChild=0;

    }

    for(i=n+1; i<=m; i++)  //非叶子结点的初始化

    {

        (*ht)[i].weight=0;

        (*ht)[i].LChild=0;

        (*ht)[i].parent=0;

        (*ht)[i].RChild=0;

    }

    cout<<endl;

    cout<<"哈夫曼树为: "<<endl;

    for(i=n+1; i<=m; i++)  //创建非叶子结点,建哈夫曼树

    {

        //在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2

        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;

        cout<<(*ht)[i].weight<<" < "<<(*ht)[s1].weight<<" , "<<(*ht)[s2].weight<<" > ";

    }

   cout<<endl;

}

//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码

void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)

{

    char *cd;  //定义的存放编码的空间

    int a[100];

    int i,start,p,w=0;

    unsigned int c;

    hc=(HuffmanCode *)malloc((n+1)*sizeof(char *));  //分配n个编码的头指针

    cd=(char *)malloc(n*sizeof(char));  //分配求当前编码的工作空间

    cd[n-1]='\0';  //从右向左逐位存放编码,首先存放编码结束符

    for(i=1; i<=n; i++)  //求n个叶子结点对应的哈夫曼编码

    {

        a[i]=0;

        start=n-1;  //起始指针位置在最右边

        for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent)  //从叶子到根结点求编码

        {

            if( (*ht)[p].LChild==c)

            {

                cd[--start]='1';  //左分支标1

                a[i]++;

            }

            else

            {

                cd[--start]='0';  //右分支标0

                a[i]++;

            }

        }

        hc[i]=(char *)malloc((n-start)*sizeof(char));  //为第i个编码分配空间

        strcpy(hc[i],&cd[start]);    //将cd复制编码到hc

    }

    free(cd);

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

        cout<<"权值为"<<(*ht)[i].weight<<"的哈夫曼编码为:   "<<hc[i]<<endl;

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

        w+=(*ht)[i].weight*a[i];

    cout<<"带权路径为:   "<<w<<endl;

}

int main()

{

    HuffmanTree HT;

    HuffmanCode HC;

    int *w,i,n,wei;

    cout<<"请输入结点个数: ";

    cin>>n;

    w=(int *)malloc((n+1)*sizeof(int));

    cout<<endl;

    cout<<" 输入这"<<n<<"个元素的权值: "<<endl;

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

    {

        cout<<i<<" ";

        fflush(stdin);

        cin>>wei;

        w[i]=wei;

    }

    CrtHuffmanTree(&HT,w,n);

    CrtHuffmanCode(&HT,&HC,n);

    return 0;

}


3. 运行结果(截图)

4. 算法分析

哈夫曼编码算法:每次将集合中两个权值最小的二叉树合并成一棵新二叉树,n-1次合并后,成为最终的一棵哈夫曼树。这既是贪心法的思想:从某一个最初状态出发,根据当前的局部最优策略,以满足约束方程为条件,以使目标函数最快(或最慢)为原则,在候选集合中进行一系列的选择,以便尽快构成问题的可行解。每次选择两个权值最小的二叉树时,规定了较小的为左子树。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值