数组实现哈夫曼树以及输出哈夫曼编码

今天来写一份作业,是这周的ds实验作业

#include <iostream>
#include <string.h>
using namespace std;
typedef struct{
    int weight;
    int parent,lc,rc;
}HTnode,*HManTree;

typedef char**Huffmancode;
void Select(HManTree HT,int t,int &s1,int &s2);

首先把每个结点的结构定义出来,包括当前节点的权重父节点左子树右子树的位置
Huffmancode用以存储每个叶子节点的haffman编码
声明Select函数

haffman树创建以及huffman编码生成的主要函数实现

void HuffmanCoding(HManTree&HT,Huffmancode&HC,int*w,int n)

HT是存放Huffman树的数组,HC是存Huffman编码,w传入权重,n为叶子节点个数

if(n<=1) return;
    int m=2*n-1;
    HT=new HTnode[m+1];
    int i=1;
    HManTree p=HT;
    for(;i<=n;++i,++p,++w)  //创建森林,用数组实现哈夫曼树
    {

        *p={*w,0,0,0};
        cout<<p->weight<<endl;
    }
    for(i=n+1;i<=m;++i,++p)    //除了前n个是森林,后续用来存放非叶子节点,每个都初始化为0
    {
        *p={0,0,0,0};
    }

m=2*n-1,是整个Huffman树的节点总数,开出空间
进行n次循环,将前n个结点权重初始化,其余属性置为0
然后后续结点,四个属性均置为0

for(i=n;i<=m;++i)     //填满从n+1到m的位置
    {   
        int s1,s2;
        Select(HT,i,s1,s2);  //从当前结点上一结点的总表中挑选出最小的两个结点,构成新结点
        HT[s1].parent=i;    //更新两个结点的父节点
        HT[s2].parent=i;
        HT[i].lc=s1;    //更新新节点的左子树和右子树
        HT[i].rc=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;  //更新父节点的权重,是两子树之和
    }//当循环结束,则哈夫曼树构建完成,根节点是HT[m]
    HT[m].parent=0;

从HT[n]开始创建结点,每次创建时,从当前位置之前的权重中搜索到最小的两个值,分别作为左子树和右子树,select函数返回的s1,s2的值就是两个最小值的位置
将两个最小值位置的父节点置为当前结点,当前节点的左右子树改为这两个结点
当前结点的权重为两个最小值的和
将根结点的父节点置为0

HC=new char*[n+1];  //n+1个字符串
    char*cd=new char[n];       //cd是长度为n的字符串
    cd[n-1]='\0';   //cd的末尾是\0
    for(int i=0;i<n;++i)
    {
         //前n个位置存放的是叶子节点,因此从这n个位子开始向父节点遍历
        int start=n-1;  //倒着填入哈夫曼编码
        int c=i;    
        int f=HT[i].parent;
        for(;f!=0;c=f,f=HT[f].parent)
        {
            if(HT[f].lc==c) cd[--start]='0';    //如果是父节点的左子树,就
            else cd[--start]='1';
        }
        HC[i]=new char[n-start];    //HC是二维字符串,HC[i]代表前n个结点中的第i个结点作为叶子节点的哈夫曼编码,此步开出来
        strcpy(HC[i],&cd[start]);     //赋值

    }
    delete []cd;

申请空间存放Huffman编码,给HC
cd作为每次循环储存每个叶子结点的Huffman编码的存储器
从叶子结点开始逐个读取其父节点,如果检验完成是父节点的左子树,则置为0,如果是右子树,置为1
因为是从叶子节点开始向根结点回溯的,因此在记录Huffman编码的时候要倒着记录
为HC[i]开出[n-start]大小的空间(因为每次存一个Huffman编码,start都会减一)
然后存入HT[i]的Huffman编码
把用作临时存储器的cd释放掉

void Select(HManTree HT,int t,int &s1,int &s2)
{
    int value1=1<<30;
    int value2=1<<30;
    for(int i=0;i<t;i++)
    {   
        if(HT[i].parent!=0) continue;
        if(value1>HT[i].weight)
        {
            if(value2>value1)
            {
                value2=value1;
                s2=s1;
            }
            value1=HT[i].weight;
            s1=i;
        }
        if(i!=s1)
        {
            if(value2>HT[i].weight)
            {
                value2=HT[i].weight;
                s2=i;
            }
        }
    }
}

之后是select函数的实现
在循环中如果检测到该节点已有父节点,则continue
要注意一点就是,如果用value1存放最小值,value2存放除最小值以外的最小值,在循环中会遇到,value1遇到了一个更小的值,把value1原本的值扔掉了,这时候需要检测value1要扔掉的值是否比value2要小,如果是,那么这个值不能扔掉,而应该给value2,替换掉value2的较大值

 if(value1>HT[i].weight)
        {
            if(value2>value1)
            {
                value2=value1;
                s2=s1;
            }
            value1=HT[i].weight;
            s1=i;
        }

就是这部分,检测value1扔掉的值是否小于value2的值

int main()
{
    int nums[10]={2,4,2,1,3,2,6,9,7,8};
    HManTree HT;
    Huffmancode HC;
    int a=sizeof(nums)/4;
    HuffmanCoding(HT,HC,nums,a);
    for(int i=0;i<a;++i)
    {
        cout<<HC[i]<<endl;
    }
    return 0;
}

测试数据
输出结果
总的来说,借由数组实现Huffman树并输出Huffman编码不算太难,理清思路,注意select函数的实现,基本就没有问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值