哈夫曼编/译码器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define CODE_LENGTH 100 //编码的最大长度


/*该结构体用来存储哈夫曼树的节点*/
typedef struct node_structure
{
    int weight; //权值
    char ch;    //节点的字符信息
    struct node_structure * leftchild;  //坐子树
    struct node_structure * rightchild; //右子树


}HuffmanTreeNode,*HFMTNodePointer;


/*该结构体用来存储从终端读入的叶子节点的字符和权值信息*/
typedef struct leafnode_structure
{
    HuffmanTreeNode data;   //哈夫曼树节点
    struct leafnode_structure * next;   //指向下一个节点


}LeafNode,*LeafNodePointer;


/*该结构体用来存储编码信息*/
typedef struct coding_structure
{
    char ch;    //叶子节点的字符信息
    char code[CODE_LENGTH]; //该叶子节点对应的编码
    struct coding_structure * next; //指向下一个叶子节点


}Coding, * CodingPointer;


void GetInfor(LeafNodePointer &info);   //读入叶子节点
void HuffmanTreeCreating(HFMTNodePointer &rootnode,LeafNodePointer &info);  //创建哈夫曼树
void PreOrderTraverse(HuffmanTreeNode *node);   //前序遍历
void InOrderTraverse(HuffmanTreeNode *node);    //中序遍历
void PostOrderTraverse(HuffmanTreeNode *node);  //后序遍历
void PrintHuffmanTree(HuffmanTreeNode *node);   //打印哈夫曼树
void GetCode(HuffmanTreeNode * rootnode,CodingPointer &codetable,char *temp);   //求各叶子节点编码
void Encording(Coding *table);  //编码
void Decording(HuffmanTreeNode * rootnode); //译码


int main()
{
    printf("--------------------初始化---------------------\n");
    /*变量初始化*/
    char _code[CODE_LENGTH];    //编码缓冲区
    int i;
    for(i=0;i<CODE_LENGTH;i++)
        _code[i]='\0';
    HuffmanTreeNode *TreeRoot=NULL; //哈夫曼树的根节点指针
    LeafNode *NodeInfo=NULL;    //指向叶子节点所组成的链表
    Coding * CodeTable=NULL;    //指向叶子节点编码信息所组成的链表


    GetInfor(NodeInfo);     //从终端读入叶子节点字符和权值
    HuffmanTreeCreating(TreeRoot,NodeInfo); //创建哈夫曼树
    GetCode(TreeRoot,CodeTable,_code);  //遍历树,存储编码


    printf("\n-------------------编码&译码-------------------\n");
    Encording(CodeTable);   //编码
    Decording(TreeRoot);    //译码


    printf("\n--------------打印各叶子节点编码---------------\n");
    while(CodeTable!=NULL)
    {
        printf("%c : %s\n",CodeTable->ch,CodeTable->code);
        CodeTable = CodeTable->next;
    }
    printf("\n-----------------打印哈夫曼树------------------\n");
    PrintHuffmanTree(TreeRoot); //按照三种遍历顺序打印节点权值
    printf("\n");
    return 0;
}


/*************************************************
*
*功能简介:从终端读取字符和权值
*          根据权值的大小顺序插入链表中
*参数简介:info:上述链表指针引用,
*                 指向叶子节点所组成的链表
*返回值:  无返回值
*
*************************************************/
void GetInfor(LeafNodePointer &info)
{
    int n,i;
    LeafNode * temp;
    LeafNode * check;
    info=NULL;
    /*读入叶子节点数量*/
    printf("请输入字符数量:\n");
    scanf("%d",&n);
    getchar();
    while(n<2)
    {
        printf("数量不正确,请重新输入:\n");
        scanf("%d",&n);
        getchar();
    }


    /*读取&排序*/
    printf("请输入字符和权值(形如:a,10 b,20 ... ):\n");
    for(i=0;i<n;i++)
    {
        check = info;
        if((temp=(LeafNode *)malloc(sizeof(LeafNode)))!=NULL)
        {
            /*读入叶子节点信息*/
            if(i<n-1)
                scanf("%c,%d ",&((temp->data).ch),&((temp->data).weight));
            else
                scanf("%c,%d",&((temp->data).ch),&((temp->data).weight));


            //因为是叶子节点,所以左右子树指针为空
            (temp->data).leftchild=NULL;
            (temp->data).rightchild=NULL;


            /*根据权值大小顺序插入链表中,越靠近头指针,权值越小*/
            if(check==NULL||(check->data).weight>(temp->data).weight)
            {
                temp->next = info;
                info=temp;
            }
            else
            {
                while(1)
                {
                    if((check->next!=NULL&&(check->next->data).weight>(temp->data).weight)||check->next==NULL)
                    {
                        temp->next = check->next;
                        check->next = temp;
                        break;
                    }
                    check=check->next;
                }
            }
        }
        else
            printf("内存分配异常!\n");
    }
    getchar();  //读取多余换行符
}


/*************************************************
*
*功能简介:创建哈夫曼树
*参数简介:rootnode:根节点指针的引用
*           info:字符和权值信息指针的引用
*返回值:   无返回值
*
*************************************************/
void HuffmanTreeCreating(HFMTNodePointer &rootnode,LeafNodePointer &info)
{
    LeafNode *root;
    LeafNode *check;
    while(1)
    {
        /*构造权值最轻的两个叶子节点的父节点。因为函数GetInfor已
        经排序,所以最靠近叶子节点链表头指针的两个节点权值最轻*/
         if((root=(LeafNode *)malloc(sizeof(LeafNode)))!=NULL)
         {
            root->data.ch='*';  //非叶子节点的字符默认为‘*’
            root->data.weight=info->data.weight+info->next->data.weight;    //父节点的权值等于两个子节点权值之和
            root->data.rightchild=&(info->data);    //指向子节点
            root->data.leftchild=&(info->next->data);
            info=info->next->next;//头指针指向第三个节点(将子节点从叶子节点链表中删除)


            check=info;
            if(check==NULL)//此时构造完成,因为没有零散的节点
                break;


            /*将上述构造的父节点按权值大小顺序插入节点链表*/
            if((check->data).weight>(root->data).weight)
            {
                root->next = info;
                info=root;
            }
            else
            {
                while(1)
                {
                    if((check->next!=NULL&&(check->next->data).weight>(root->data).weight)||check->next==NULL)
                    {
                        root->next = check->next;
                        check->next = root;
                        break;
                    }
                    check=check->next;
                }
            }
        }
    }
    rootnode=&(root->data);
}


/*************************************************
*
*功能简介:前序遍历哈夫曼树
*参数简介:node:根节点指针的引用
*返回值:   无返回值
*
*************************************************/
void PreOrderTraverse(HuffmanTreeNode *node)
{
    if(node!=NULL)
    {
        printf("%d ",node->weight);
        PreOrderTraverse(node->leftchild);
        PreOrderTraverse(node->rightchild);
    }
}


/*************************************************
*
*功能简介:中序遍历哈夫曼树
*参数简介:node:根节点指针的引用
*返回值:   无返回值
*
*************************************************/
void InOrderTraverse(HuffmanTreeNode *node)
{
    if(node!=NULL)
    {
        InOrderTraverse(node->leftchild);
        printf("%d ",node->weight);
        InOrderTraverse(node->rightchild);
    }
}


/*************************************************
*
*功能简介:中序遍历哈夫曼树
*参数简介:node:根节点指针的引用
*返回值:   无返回值
*
*************************************************/
void PostOrderTraverse(HuffmanTreeNode *node)
{
    if(node!=NULL)
    {
        PostOrderTraverse(node->leftchild);
        PostOrderTraverse(node->rightchild);
        printf("%d ",node->weight);
    }
}


/*************************************************
*
*功能简介:按照三种遍历顺序打印节点权值
*参数简介:node:根节点指针的引用
*返回值:   无返回值
*
*************************************************/
void PrintHuffmanTree(HuffmanTreeNode *node)
{
    printf("用三种遍历方法打印树的各个节点权值:\n");
    printf("\t前序遍历:");
    PreOrderTraverse(node);
    printf("\n");
    printf("\t中序遍历:");
    InOrderTraverse(node);
    printf("\n");
    printf("\t后序遍历:");
    PostOrderTraverse(node);
}


/*************************************************
*
*功能简介:前序遍历哈夫曼树,并将沿途的编码信息
*           存入缓冲区,当访问叶子节点时将缓冲区
*           的编码和节点字符信息存入链表中
*参数简介:rootnode:根节点指针的引用
*           codetable:指向上述链表
*           temp:指向上述缓冲区
*返回值:   无返回值
*
*************************************************/
void GetCode(HuffmanTreeNode * rootnode,CodingPointer &codetable,char *temp)
{
    int i;
    CodingPointer codetemp;
    if(rootnode!=NULL)
    {
        if(rootnode->leftchild==NULL&&rootnode->rightchild==NULL)   //叶子节点
        {
            /*运用头插法向链表中插入新元素,并把编码信息记录到该元素*/
            if((codetemp=(CodingPointer)malloc(sizeof(Coding)))!=NULL)
            {
                codetemp->ch=rootnode->ch;
                strcpy(codetemp->code,temp);
                codetemp->next=codetable;
                codetable=codetemp;
            }
        }
        else
        {
            /*前序遍历*/
            i=strlen(temp);
            temp[i]='0';    //遍历左子树编码为0
            temp[i+1]='\0';
            GetCode(rootnode->leftchild,codetable,temp);
            temp[strlen(temp)-1]='1';   //遍历右子树编码为1
            GetCode(rootnode->rightchild,codetable,temp);
            temp[strlen(temp)-1]='\0';   //从右子树返回,退格
        }
    }
}


/*************************************************
*
*功能简介:编码。从终端读入字符信息,查询
*           编码信息所组成的链表,打印编码
*参数简介:table:指向编码链表
*返回值:   无返回值
*
*************************************************/
void Encording(Coding *table)
{
    char ch;
    Coding *temp;
    //getchar();
    printf("请输入需要编码的字符集(以‘#’结束):\n");


    while((ch=getchar())!='#')  //判断是否读完
    {
        /*查询链表*/
        temp=table;
        while(ch!=temp->ch&&temp!=NULL)
            temp=temp->next;
        if(ch==temp->ch)
            printf("%s",temp->code);
    }
    getchar();  //读取多余换行符
    printf("\n");
}


/*************************************************
*
*功能简介:译码。从终端读入编码信息。根据
*           编码信息访问子树,‘0’访问左子树,
*           ‘1’访问右子树。如若访问的是叶子
*           节点就输出节点字符信息,下一轮循
*           环并从根节点开始访问子树。
*参数简介:rootnode:根节点指针的引用
*返回值:   无返回值
*
*************************************************/
void Decording(HuffmanTreeNode * rootnode)
{
    HuffmanTreeNode *temp=rootnode;
    char ch;
    printf("请输入需要译码的字符数字(0,1;以'#'结束)\n");
    while((ch=getchar())!='#')  //判断是否读完
    {
        if(temp->leftchild!=NULL&&temp->rightchild!=NULL)   //非叶子节点
        {
            /*访问子树*/
            if(ch=='0')
                temp=temp->leftchild;
            else if(ch=='1')
                temp=temp->rightchild;
        }
        if(temp->leftchild==NULL&&temp->rightchild==NULL)   //叶子节点
        {
            printf("%c",temp->ch);  //打印字符
            temp=rootnode;  //从根节点开始
        }
    }
    getchar();  //读取多余换行符
    printf("\n");
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[ 问题描述 ] 利用哈夫曼编码进行信息通讯可以大大提高信道利用率, 缩短信息传输时间,降低传输成本。但是, 这要求在发送端通过一个码系统对待传数据预先码; 在接收端将传来的数据进行译码( 复原 )。对于双工信道( 即可以双向传输信息的信道 ), 每端都需要一个完整的/译码系统。试为这样的信息收发站写一个哈夫曼码的译码系统。 [ 基本要求 ] 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小 n,及 n 个字符和 n 个权值,建立哈夫曼树, 并将它存于文件 hfmtree 中。 (2) C:码(Coding)。利用已建好的哈夫曼树(如不在内存, 则从文件 hfmtree 中读入), 对文件 tobetrans 中的正文进行码, 然后将结果存入 codefile 中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件 codefile 中的代码进行译码, 结果存入文件 textfile 中。 (4) P:印代码文件(Print)。将文件 codefile 以紧凑格式显示在终端上, 每行 50 个代码。同时将此字符形式的码文件写入文件 codeprint 中。 (5) T:印哈夫曼树(Tree print)。将已在内存中的哈夫曼树以直观的方式( 树或凹入表行式 )显示在终端上, 同时将此字符形式的哈夫曼树写入文件 treeprint 中。 [ 测试数据 ] (1)利用教科书 6-2 中的数据调试程序。 (2)用下表中给出的字符集和频度的实际统计数据建立哈夫曼树, 并实现以下报 文的码和译码: "THIS PROGRAM IS MY FAVORITE". 字符 A B C D E F G H I J 频度 186 64 13 22 32 103 21 15 47 57 1 字符 K L M N O P Q R S T U 频度 5 32 20 57 63 15 1 48 51 80 23 字符 V W X Y Z 频度 8 18 1 16 1 [ 实现提示 ] (1) 文件 codefile 的基类型可以设为子界型 bit=0..1。 (2) 用户界面可以设计为"菜单"方式: 显示上述功能符号, 再加上"E",表示结束运行 End, 请用户键入一个选择功能符。此功能执行完毕后再显示此菜单, 直至某次用户选择了 "E"为止。 (3) 在程序的一次执行过程中, 第一次执行 I,D 或 C 命令之后, 哈符曼树已经在内存了, 不必再读入。每次执行中不一定执行 I 命令, 因为文件 hfmtree 可能早己建好。 [ 选作内容 ] (1) 上述 codefile 的基类型实际上占用了存放一个整数的空间, 只起到示意或模拟的作用。现使 codefile 的基类型 package=integer, 把哈夫曼码紧缩到一个整型变量中去, 最大限度地利用码点存储能力, 试改写你的系统。 (2) 修改你的系统, 实现对你的系统的源程序的码和译码。( 主要是将行尾符/译码问题 ) (3) 实现各个转换操作的源/目文件, 均由用户在选择此操作时指定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值