数据结构(五)——树和二叉树之Huffman编码

本文介绍了如何计算字符串权重,构建Huffman树,以及为树节点分配编码的过程。通过动态数组构建2n-1个节点的二叉树,通过合并最小权重节点形成最小权重树。接着,使用二维数组记录编码,并利用BFS遍历来计算Huffman编码。最后,提供了源代码以展示具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义的结构体

typedef struct node
{
    char elem;
    float weight;
    int loc;
    int lch,rch,parent;
}Node;

计算字符串中的权重

  • 没什么技巧,直接计算
int CalculateWeight(char a[],char b[],float weight[])
{
    int i=0,j;
    int len1=0,len2=0;
    while(a[i]!='\0')//计算输入字符的个数
    {
        i++;
    }
    len1=i;//记录输入字符的个数
    i=0;
    while(a[i]!='\0')//计算输入数据的权重
    {
        for(j=0;j<len2;j++)//判断已记录数据是否有该数据
        {
            if(a[i]==b[j])
            {
                i++;
                weight[j]++;
                break;
            }
        }
        if(j==len2)//已记录数据不存在该字符则新增该字符
        {
            b[len2]=a[i];
            weight[len2]++;
            len2++;
            i++;
        }
    }
    for(i=0;i<len2;i++)
    {
        weight[i]=weight[i]/len1;//计算所占百分比
    }
    return len2;//返回不重复的字符数
}

构造Huffman树

  • 由于叶结点的数目n是已经知道的,故我可以知道所要构建的二叉树的结点一共为2n-1个,故可以申请数量为2n-1的动态数组,其中每个数组中的元素为一个Node。
  • 之后将数组初始化,并且将二叉树的叶结点赋值到新开的数组T[ ]中。
  • 之后根据寻找无父母的结点中的最小权重点和次小权重点进行合并成一个新的结点,将新的结点依次存储在T[ ]数组中,即构建出了一棵权重最小的二叉树。

为Huffman树赋值编码

  • 申明一个二维数组codenum[ ] [ ] 数组用于存储编码,初始化该数组。
  • 申请一个队列,长度为Huffman树的结点数,辅助计算编码
  • 设根结点的编码为空,其中,向左子树走一步则编码为0,向右子树走一步则编码为1(在编码的计算中,运用到了BFS遍历。
void Codenum(Node T[],int l)
{
    char codenum[MAX][MAX];//用于存储前缀编码
    int top=0,tail=0;
    int len=0,len1=0,len2=0;
    Node *a;
    Node temp;
    memset(codenum,0,sizeof(codenum));
    len=2*l-1;
    a=(Node*)malloc(len*sizeof(int));//用于充当队列
    a[tail].lch=T[len-1].lch;//队首赋初值
    a[tail].rch=T[len-1].rch;
    tail++;//队尾+1
    while(top!=tail)
    {
        if(len==1)
        {
            temp.lch=a[top].lch;
            temp.rch=a[top].rch;
            temp.loc=len-1;
            codenum[len-1][0]='0';
            top++;
        }
        else
        {
            temp.lch=a[top].lch;
            temp.rch=a[top].rch;
            temp.loc=a[top].loc;
            top++;//出队操作
            if(temp.lch!=-1)//查看是否有左子树
            {
                a[tail].lch=T[temp.lch].lch;
                a[tail].rch=T[temp.lch].rch;
                a[tail].loc=temp.lch;
                tail++;
                strcpy(codenum[temp.lch],codenum[T[temp.lch].parent]);//编码计算
                len1=strlen(codenum[temp.lch]);
                codenum[temp.lch][len1]='0';
            }
            if(temp.rch!=-1)//查看是否有右子树
            {
                a[tail].lch=T[temp.rch].lch;
                a[tail].rch=T[temp.rch].rch;
                a[tail].loc=temp.rch;
                tail++;
                strcpy(codenum[temp.rch],codenum[T[temp.rch].parent]);//编码计算
                len2=strlen(codenum[temp.rch]);
                codenum[temp.rch][len2]='1';
            }
        }
        if(temp.rch==-1&&temp.lch==-1)
        {
            printf("%c:",T[temp.loc].elem);//打印编码
            puts(codenum[temp.loc]);
        }
    }
}

源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 1000
typedef struct node
{
    char elem;
    float weight;
    int loc;
    int lch,rch,parent;
}Node;

int CalculateWeight(char a[],char b[],float weight[]);//输入字符串计算权重,返回字符串长度
Node *Huffmantree(char b[],float weight[],int len);//计算huffman树
void Codenum(Node T[],int l);//计算前缀编码并输出

int main()
{
    char a[MAX]={'\0'},b[MAX]={'\0'};
    float weight[MAX]={0};
    int len;
    Node *T;
    printf("请输入需要编译的字符串(以输入回车为截止):\n");
    gets(a);
    len=CalculateWeight(a,b,weight);
    T=Huffmantree(b,weight,len);
    Codenum(T,len);
    printf("以上为编码表\n");
    return 0;
}

int CalculateWeight(char a[],char b[],float weight[])
{
    int i=0,j;
    int len1=0,len2=0;
    while(a[i]!='\0')//计算输入字符的个数
    {
        i++;
    }
    len1=i;//记录输入字符的个数
    i=0;
    while(a[i]!='\0')//计算输入数据的权重
    {
        for(j=0;j<len2;j++)//判断已记录数据是否有该数据
        {
            if(a[i]==b[j])
            {
                i++;
                weight[j]++;
                break;
            }
        }
        if(j==len2)//已记录数据不存在该字符则新增该字符
        {
            b[len2]=a[i];
            weight[len2]++;
            len2++;
            i++;
        }
    }
    for(i=0;i<len2;i++)
    {
        weight[i]=weight[i]/len1;//计算所占百分比
    }
    return len2;//返回不重复的字符数
}

Node *Huffmantree(char b[],float weight[],int len)
{
    int i,j;
    int p1=-1,p2=-1;
    float small1=1,small2=1;
    int Tlen;
    Node *T;
    T=(Node*)malloc((2*len-1)*sizeof(Node));//动态申请空间
    Tlen=2*len-1;//记录二叉树的总结点个数
    for(i=0;i<Tlen;i++)//初始化
    {
        T[i].elem='\0';
        T[i].lch=-1;
        T[i].rch=-1;
        T[i].parent=-1;
        T[i].weight=0;
    }
    for(i=0;i<len;i++)//叶结点的赋值
    {
        T[i].elem=b[i];
        T[i].weight=weight[i];
    }
    for(j=len;j<Tlen;j++)//补充二叉树
    {
        for(i=0;i<len;i++)//遍历二叉树
        {
            if(T[i].parent==-1)//判断是否有父母
            {
                if(T[i].weight<small1)//寻找权重最小点
                {
                    small2=small1;
                    p2=p1;
                    small1=T[i].weight;
                    p1=i;
                }
                else if(T[i].weight<small2)//寻找权重次小点
                {
                    small2=T[i].weight;
                    p2=i;
                }
            }
        }
        T[j].weight=T[p1].weight+T[p2].weight;//生成父母
        T[p1].parent=j;//二叉树关系的建立
        T[p2].parent=j;
        T[j].lch=p1;
        T[j].rch=p2;
        small1=1;
        small2=1;
        len++;//新增结点算入总长
    }
    return T;
}

void Codenum(Node T[],int l)
{
    char codenum[MAX][MAX];//用于存储前缀编码
    int top=0,tail=0;
    int len=0,len1=0,len2=0;
    Node *a;
    Node temp;
    memset(codenum,0,sizeof(codenum));
    len=2*l-1;
    a=(Node*)malloc(len*sizeof(int));//用于充当队列
    a[tail].lch=T[len-1].lch;//队首赋初值
    a[tail].rch=T[len-1].rch;
    tail++;//队尾+1
    while(top!=tail)
    {
        if(len==1)
        {
            temp.lch=a[top].lch;
            temp.rch=a[top].rch;
            temp.loc=len-1;
            codenum[len-1][0]='0';
            top++;
        }
        else
        {
            temp.lch=a[top].lch;
            temp.rch=a[top].rch;
            temp.loc=a[top].loc;
            top++;//出队操作
            if(temp.lch!=-1)//查看是否有左子树
            {
                a[tail].lch=T[temp.lch].lch;
                a[tail].rch=T[temp.lch].rch;
                a[tail].loc=temp.lch;
                tail++;
                strcpy(codenum[temp.lch],codenum[T[temp.lch].parent]);//编码计算
                len1=strlen(codenum[temp.lch]);
                codenum[temp.lch][len1]='0';
            }
            if(temp.rch!=-1)//查看是否有右子树
            {
                a[tail].lch=T[temp.rch].lch;
                a[tail].rch=T[temp.rch].rch;
                a[tail].loc=temp.rch;
                tail++;
                strcpy(codenum[temp.rch],codenum[T[temp.rch].parent]);//编码计算
                len2=strlen(codenum[temp.rch]);
                codenum[temp.rch][len2]='1';
            }
        }
        if(temp.rch==-1&&temp.lch==-1)
        {
            printf("%c:",T[temp.loc].elem);//打印编码
            puts(codenum[temp.loc]);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值