哈夫曼编码

题目来源:数据结构实验  NOJ 033 哈夫曼编/译码器

Description
写一个哈夫曼码的编/译码系统,要求能对要传输的报文进行编码和解码。构造哈夫曼树时,权值小的放左子树,权值大的放右子树,编码时右子树编码为1,左子树编码为0。

Input
输入表示字符集大小为n(n <= 100)的正整数,以及n个字符和n个权值(正整数,值越大表示该字符出现的概率越大);输入串长小于或等于100的目标报文。

Output
经过编码后的二进制码,占一行;
以及对应解码后的报文,占一行;
最后输出一个回车符

Sample Input 


5 a b c d e 12 40 15 8 25
bbbaddeccbbb


Sample Output


00011111110111010110110000

 

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;

int pc = 1;

typedef struct HtNode //哈夫曼树结点
{
    int weight;
    int parent, lchild, rchild;
    int flag;
}HtNode;

typedef struct HtTree //哈夫曼树的建立
{
    HtNode ht[3000];
    int root;
}phtree;

void select(phtree* pht,int pos,int *x1,int *x2) //在哈夫曼树中查找当前权值最小的两个结点
{
    int m1, m2; //m1为权值最小的结点的权值,m2为权值次小的结点权值
    m1 = m2 = 10000;
    for(int i = 1;i < pos;i++)
    {
        if(pht->ht[i].weight < m1 && pht->ht[i].parent == 0)
        {
            m2 = m1;  //最小值变更
            *x2 = *x1;
            m1 = pht->ht[i].weight;  //记录最小的权值
            *x1 = i;   //记录最小权值对应的结点编号
        }
        else if(pht->ht[i].weight < m2 && pht->ht[i].parent == 0 && pht->ht[i].weight > m1)
        {
            m2 = pht->ht[i].weight;  //记录次小的权值
            *x2 = i;  //记录次小权值对应的结点编号
        }
    }
}

phtree *huffman(int n,int *w) //构建哈夫曼树
{
    phtree* pht;
    int i, x1, x2;
    pht = (phtree* )malloc(sizeof(phtree));

    pht->ht[0].flag = 2;
    pht->ht[0].lchild = 0;
    pht->ht[0].rchild = 0;
    pht->ht[0].parent = 0;
    pht->ht[0].weight = 0;
    for(i = 2*n;i < 3000;i++) //2*n-1后的结点为无用结点
    {
        pht->ht[i].flag = 2;
        pht->ht[i].lchild = 0;
        pht->ht[0].rchild = 0;
        pht->ht[0].parent = 0;
        pht->ht[0].weight = 0;
    }

    for(i = 1;i <= 2*n-1;i++)  //建立空结点
    {
        pht->ht[i].lchild = 0;
        pht->ht[i].rchild = 0;
        pht->ht[i].parent = 0;
        pht->ht[i].flag = 2;
        if(i <= n)  //将前n个结点赋予权值
        {
            pht->ht[i].weight = w[i];
        }
        else
        {
            pht->ht[i].weight = 0;
        }
    }


    for(i = 1;i < n;i++) //构建哈夫曼树
    {
        select(pht,n+i,&x1,&x2);  //查询当前权值最小的两个结点,用指针保存结点编号
        pht->ht[x1].parent = n+i;  //第n+i为空结点,故将此结点作为两个权值最小结点的父结点
        pht->ht[x1].flag = 0;
        pht->ht[x2].parent = n+i;
        pht->ht[x2].flag = 1;
        pht->ht[n+i].weight = pht->ht[x1].weight + pht->ht[x2].weight;  //更新父结点的权值
        pht->ht[n+i].lchild = x1;  //更新父结点的子结点
        pht->ht[n+i].rchild = x2;
        pht->root = n+i;
    }
    return pht;
}

void traversehuffman(phtree *t,int n,int pcode[][100])  //遍历创建每一个字符对应的哈夫曼编码
{
    int i = 1;
    HtNode node;
    node = t->ht[pc];
    while(node.parent != 0)  //前n个结点为子结点,通过不断遍历其父结点,从而生成当前字符对应的哈夫曼编码
    {
        pcode[pc][i] = node.flag;
        node = t->ht[node.parent];
        i++;
    }
    pc++;  //查找下一个字符的哈夫曼编码
}

void output(int pcode[][100],int n,char *x)  //输出哈夫曼编码和译码结果
{
    int i, j, k, cnt;
    int code[100][100];
    for(i =0;i < 100;i++)
    {
        for(j = 0;j < 100;j++)
        {
            code[i][j] = -1;
        }
    }
    for(i = 1;i <= n;i++)  //遍历将pcode值为-1的无用编码去除
    {
        for(cnt = 1;pcode[i][cnt+1] == 1 || pcode[i][cnt+1] == 0;cnt++);
        k = cnt;
        for(j = 1;j<= k;j++)
        {
            code[i][j] = pcode[i][cnt--];
        }
    }

    char s[100], c;
    scanf("%s",s);
    int f = strlen(s);
    for(i = 0;i <= f;i++)    //遍历编码序列
	{
        for(j = 1;j <= n;j++)  //遍历字符序列
        {
            if(s[i] == x[j])  //找到对应字符
            {
                for(k = 1;code[j][k] == 0 || code[j][k] == 1;k++)  //输出对应的哈夫曼编码
                {
                    printf("%d",code[j][k]);
                }
            }
        }
    }

    printf("\n%s\n",s);
}

int main()
{
    int i, n, cnt = 1;
    char c;
    int w[101];  //记录结点的权值
    char s[101];  //记录结点的信息
    int pcode[100][100];
    for(i = 0;i < 100;i++)
    {
        for(int j = 0;j < 100;j++)
        {
            pcode[i][j] = -1;
        }

    }
    scanf("%d",&n);
    while(1)  //读入字符
    {
        scanf("%c",&c);
        if(cnt == n+1)
        {
            break;
        }
        else if(c != ' ')
        {
            s[cnt++] = c;
        }
    }
    for(i = 1;i <= n;i++)  //读入字符对应的权值
    {
        scanf("%d",&w[i]);
    }
    phtree* pht;
    pht = huffman(n,w);  //构建哈夫曼树
    for(i = 1;i <= n;i++)  //遍历创建每一个字符对应的哈夫曼编码
    {
        traversehuffman(pht,n,pcode);
    }
    output(pcode,n,s);  //输出哈夫曼编码和译码结果
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值