哈夫曼编/译码演示系统的C程序



/*====================================================*/
/*                                                    */
/* huffman.c(pp) - a huffman arithmetic demonstration */
/* smith_135@163.com                                  */
/* QQ: 58101543                                       */
/* version 0.9                                        */
/* copyright (c) meteor135                            */
/* 2004.7.13  v0.10                                   */
/* 2004.11.19 v0.11                                   */
/*                                                    */
/*====================================================*/

#include<stdio.h>   /* for size_t, printf()            */
#include<conio.h>   /* for getch()                     */
#include<ctype.h>   /* for tolower()                   */
#include<malloc.h>  /* for malloc(), calloc(), free()  */
#include<string.h>  /* for memmove(), strcpy()         */

/*树结构和全局结构指针*/
struct node
{
    int num;/*结点编号*/
    char c;
    int weight;
    int parent;
    int lchild,rchild;
} * ht;

/*常量文件名*/
const char *TableFileName    = "HfmTbl.txt";
const char *SourceFileName   = "SrcText.txt";
const char *CodeFileName     = "HfmCode.txt";
const char *DecodeFileName   = "DecodeText.txt";
const char *TreeViewFileName = "TreeView.txt";
/*打印格式字符串常量*/
const char *PrintFormatStr = "%4d %13c %13d %13d %13d %13d/r/n";
/*读取格式字符串常量*/
const char *ReadFormatStr  = "%d %c %d %d %d %d";
/*打印参数宏*/
#define PRINT_PARAM(i) ht[(i)].num,ht[(i)].c,ht[(i)].weight,/
        ht[(i)].parent,ht[(i)].lchild,ht[(i)].rchild
/*读取参数宏*/
#define READ_PARAM(i) &ht[(i)].num,&ht[(i)].c,&ht[(i)].weight,/
        &ht[(i)].parent,&ht[(i)].lchild,&ht[(i)].rchild
        
/*打印格式和参数宏*/
#define READ_FORMAT_PARAM(i) ReadFormatStr,READ_PARAM(i)
/*读取格式和参数宏*/
#define PRINT_FORMAT_PARAM(i) PrintFormatStr,PRINT_PARAM(i)


/************************************************************/
/**              UTILITY FUNCTIONS                         **/
/************************************************************/
/*内存交换函数,用于结构体变量交换*/
void MemSwap(void *buf1, void *buf2, size_t buflen)
{
    if(buf1!=buf2)
    {
        void *p = malloc(buflen);
        memmove(p,buf1,buflen);
        memmove(buf1,buf2,buflen);
        memmove(buf2,p,buflen);
        free(p);
    }
}

/*打印表*/
void PrintHT(int from, int to)
{
    int i;
    for(i=from;i<=to;i++)
    {
        printf(PRINT_FORMAT_PARAM(i));
    }
    printf("/n");
}

/*选择法排序*/
void SelectSort(int from, int to)
{
    int i,j,k;
    for(i=from;i< to; i++)
    {
        for(k=i,j=i+1;j<=to;j++)
            if(ht[j].weight<ht[k].weight)
                k=j;
        if(k!=i)
            MemSwap(&ht[k],&ht[i],sizeof(struct node));
        PrintHT(from,to);
    }
}

/*释放ht*/
void free_ht()
{
    if(ht != NULL)
    {
        free(ht);
        ht = NULL;
    }
}

/*从文件读取数据保存到全局堆中,调用方要记得释放*/
int ReadFromFile()
{
    int i;
    int m;
    FILE *h;
    
    if((h=fopen(TableFileName,"r"))==NULL)
    {
        printf("cannot open %s/n", TableFileName);
        getch();
        return 0;
    }
    fscanf(h,"%d",&m);
    free_ht();
    ht=(struct node *)calloc(m+1,sizeof(struct node));
    //printf("%d/n",m);
    for(i=1;i<=m;i++)
    {
        fscanf(h,READ_FORMAT_PARAM(i));
       // printf(PRINT_FORMAT_PARAM(i));
    }
    fclose(h);
    return m;
}

/*吃掉无效的垃圾字符*/
void EatCharsUntilNewLine()
{
    while(getchar()!='/n')
        continue;
}

/*按节点序号重新排序*/
void SortByNum(int m)
{
    int i = 0, j;
    size_t len = sizeof(struct node);
    for(i = 1; i < m; i ++)
    {
        for(j = i + 1; j <= m; j ++ )
        {
            if (ht[j].num == i)
            {
                MemSwap(&ht[i],&ht[j],len);
                break;
            }
        }
    }
}
/************************************************************/
/**              COMMAND INSTRUCTION FUNCTIONS             **/
/************************************************************/
/*初始化并写入文件*/
void Initialize()
{
    int i=0,x=0,y=0,n,m;
    FILE *h;

    puts("input the number of the total char n:");
    scanf("%d",&n);
    EatCharsUntilNewLine();

    m=2*n-1;
    ht=(struct node *)calloc(m+1,sizeof(struct node));

    /*遍历叶子结点进行初始化*/
    for(i=1;i<=n;i++)
    {
        puts("input a char:");
        scanf("%c",&ht[i].c);
        EatCharsUntilNewLine();
        
        puts("input the weight of this char:");
        scanf("%d",&ht[i].weight);        
        EatCharsUntilNewLine();

        ht[i].num=i;
    }
    PrintHT(1,n);

    for(i=n+1;i<=m;i++) /*adding  branch */
    {
        ht[i].c='$';
        ht[i].num=i;
    }
    PrintHT(n+1,m);

    /*用选择法将第1到n个元素从小到大排序*/
    SelectSort(1,n);
    PrintHT(1,n);

    for(x=1,i=n+1;i<=m;i++,x+=2)
    {
        y=x+1;
        ht[x].parent=ht[y].parent=i;
        ht[i].lchild=ht[x].num;
        ht[i].rchild=ht[y].num;
        ht[i].weight=ht[x].weight+ht[y].weight;
        /*排序*/
        SelectSort(1, i);
    }

    /*察看排序效果*/
    PrintHT(1,m);
    getch();

    SortByNum(m);
    /*察看恢复序号效果*/
    PrintHT(1,m);
    getch();
    
    /*数据写入文件并输出至屏幕*/
    if((h=fopen(TableFileName,"wb"))==NULL)
    {
        printf("cannot open %s/n", TableFileName);
        getch();
        return;
    }
    printf("The huffmantree is:/n");
    printf("   number      character      weight        parent         lchild         rchild/n");

    /*首行写入数据行数*/
    fprintf(h,"%d/r/n",m);
    /*循环写入每行同时向屏幕输出*/
    for(i=1;i<=m;i++)
    {
        printf(PRINT_FORMAT_PARAM(i));
        fprintf(h,PRINT_FORMAT_PARAM(i));
    }
    free_ht();
    fclose(h);
}
/*编码并写入文件*/
void Encode()
{
    int i,mm,c,f,start;
    char wr,*cd = (char *)NULL, ch;
    char **HC = (char **)NULL;
    FILE *fp1, *fp2;
    int m = ReadFromFile();
    int n = (m+1)/2;
    
    if(HC!=NULL) free(HC);
    HC=(char **)calloc(n+1,sizeof(char *));
    if(HC==NULL) return;

    if(cd!=NULL) free(cd);
    cd=(char *)calloc(n,sizeof(char));
    if(cd==NULL) return;

    for(i=1;i<=n;i++)
    {
        start=n-1;
        for(c=i,f=ht[i].parent; f!=0; c=f,f=ht[f].parent)
        {
            /*cd[--start]='1'-(ht[f].lchild==c);*/
            if(ht[f].lchild==c)
                cd[--start]='0';
            else
                cd[--start]='1';
        }

        HC[i]=(char *)malloc((n-start)*sizeof(char));
        strcpy(HC[i],&cd[start]);
    }
    free(cd);
    
    for(i=1;i<=n;i++)
        printf("%c----%s/n",ht[i].c,HC[i]);

    printf("IF input the file tobetran select A or a, ELSE read from %s./n",SourceFileName);
    printf("PLEASE INPUT:");
    scanf("%c",&wr);

    if(wr=='a'||wr=='A')
    {
        if((fp1=fopen(SourceFileName,"w+"))==NULL)
        {
            printf("cannot open %s/n", SourceFileName);
            getch();
            return;
        }
        printf("Please input the tobetran:(end with '#')/n");
        ch=getch();
        while(ch!='#')
        {
            fputc(ch,fp1);
            putchar(ch);
            ch=getch();
        }
        rewind(fp1);
        printf("/n");
    }
    else
    {
        if((fp1=fopen(SourceFileName,"r"))==NULL)
        {
            printf("cannot open %s/n", SourceFileName);
            getch();
            return;
        }
    }
    if((fp2=fopen(CodeFileName,"w"))==NULL)
    {
        printf("cannot open %s/n", CodeFileName);
        getch();
        return;
    }
    while(!feof(fp1))
    {
        ch=fgetc(fp1);
        for(i=1;i<=n;i++)
            if(ht[i].c==ch)
                for(mm=0;HC[i][mm]!='/0';mm++)
                {
                    fputc(HC[i][mm],fp2);
                    printf("%c",HC[i][mm]);
                }
    }
    printf("/n");
    for(i=1;i<=n;i++)
        fprintf(fp1,"/r/n%c----%s",ht[i].c,HC[i]);
    for(i=1;i<=n;i++)
        fprintf(fp2,"/r/n%s----%c",HC[i],ht[i].c);
    for(i = 1; i <= n; i ++)
    {
        free(HC[i]);
    }
    free(HC);
    free_ht();
    fclose(fp1);
    fclose(fp2);
}
/*解码写入文件并输出*/
void Decode()
{
    FILE *CodeFileP, *TextFileP;
    char ch = '/0';
    int f;
    int m = ReadFromFile();
    f = m;
    if((CodeFileP=fopen(CodeFileName,"r"))==NULL)
    {
        printf("cannot open %s/n", CodeFileName);
        getch();
        return;
    }
    if((TextFileP=fopen(DecodeFileName,"w"))==NULL)
    {
        printf("cannot open %s/n", DecodeFileName);
        getch();
        return;
    }

    while(!feof(CodeFileP)&&ch!='/n')
    {
        ch=fgetc(CodeFileP);  
        if(ch=='0')
            f=ht[f].lchild;
        if(ch=='1')
            f=ht[f].rchild;
        if(!ht[f].lchild&&!ht[f].rchild)
        {
            fputc(ht[f].c,TextFileP);
            printf("%c",ht[f].c);
            f=m;
        }
    }
    free_ht();
    fclose(CodeFileP);
    fclose(TextFileP);
    printf("/n");
}
/*不解码直接输出文件中的huffman编码*/
void PrintCode()
{
    int i=0;
    char ch = 0;
    FILE *CodeFileP;
    if((CodeFileP=fopen(CodeFileName,"r"))==NULL)
    {
        printf("cannot open %s/n",CodeFileName);
        getch();
        return;
    }
    while(!feof(CodeFileP)&&ch!='/n')
    {
        if(i++==50)
        {
            printf("/n");
            i=0;
        }
        ch=fgetc(CodeFileP);
        printf("%c",ch);
    }
    printf("/n");
    fclose(CodeFileP);
}

/*输出Huffman树*/
void PrintTree()
{
    int i,k,top,p;
    int *level,*stack;
    FILE *TreePrintFileP;
    int m = ReadFromFile();
    int n = (m+1)/2;

    if((TreePrintFileP=fopen(TreeViewFileName,"w"))==NULL)
    {
        printf("cannot open %s/n", TreeViewFileName);
        getch();
        return;
    }

    printf("THE HUFFMANTREE IS :/n");
    fprintf(TreePrintFileP,"THE HUFFMANTREE IS :/n");
    level=(int *)malloc(n*sizeof(int));
    stack=(int *)malloc(n*sizeof(int));

    if(m!=0)
    {
        top=1;
        stack[top]=m;
        level[top]=3;
        while(top>0)
        {
            p=stack[top];   
            k=level[top];    
            for(i=1;i<=k;i++)
            {
                printf(" ");
                fprintf(TreePrintFileP," ");
            }
            printf("%d/n",ht[p].weight);
            fprintf(TreePrintFileP,"%d/n",ht[p].weight);
            top--;
            if(ht[p].rchild!=0)
            {
                top++;
                stack[top]=ht[p].rchild;
                level[top]=k+3;
            }
            if(ht[p].lchild!=0)
            {
                top++;    
                stack[top]=ht[p].lchild;
                level[top]=k+3;
            }
        }
    }
    free(level);
    free(stack);
    fclose(TreePrintFileP);
}

int prompt()
{
    char en;
    puts("/n******************* huffman arithmetic demonstration *****************");
    puts("i/I---Initialize     e/E---encode   p/P---print file's code");
    puts("t/T---print hfmtree  d/D---decode   q/Q---quit program");
    puts("******************* huffman arithmetic demonstration *****************");
    puts("please choose a menu item and press enter to continue.");
    printf(">");
    scanf("%c",&en);
    EatCharsUntilNewLine();
    return tolower(en);
}

void main()
{
    while(1)
    {
        switch(prompt())
        {
            case 'i':
                Initialize();
                break;
            case 'e':
                Encode();
                break;
            case 'd':
                Decode();
                break;
            case 'p':
                PrintCode();
                break;
            case 't':
                PrintTree();
                break;
            case 'q':
                free_ht();
                return;
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
问题描述: 利用哈夫曼码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个系统对待传数据预先码;在接收端将传来的数据进行译码(复原)。对于双工信道 (即可以双向传输信息的信道),每端都需要一个完整的/译码系统。试为这样的信息收发站写一个哈夫曼码的译码系统。 基本要求: 一个完整的系统应具有以下功能: (l)I:初始化 (Initialization)。从终端读入字符集大小n,及n个字符和m个权值哈夫曼树,并将它存于文件hfmtree。 (2)C:码 (Coding)。利用已好的哈夫曼树(如不在内存,则从文件hfmtree读入),对文件tobetrans的正文进行码,然后将结果存入文件codefile。 (3)D:码 (Decoding)。利用已好的哈夫曼树将文件codefile的代码进行译码,结果存入文件textfile。 (4)P:印代码文件 (Print)。将文件codefile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的码文件写入文件codeprint。 (5)T:印哈夫曼树 (Tree printing)。将已在内存哈夫曼树以直观的方式 (树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint。 实现提示 根据题目要求把程序划成5个模块,设计成菜单方式,每次执行一个模块后返回菜单。 除了初始化(I)过程外,在每次执行时都经过一次读取磁盘文件数据。这是为了如果在程序执行后一直没有进行初始化(I)过程,为了能使后面的操作顺利进行,可以通过读取旧的数据来进行工作。比如:如果程序的工作需要的字符集和权值数据是固定的,只要在安装程序时进行一次初始(I)化操作就可以了。再在次运行程序时,不管进行那项操作都可以把需要的数据读入到内存。 算法分析 本程序主要用到了三个算法。 (1)哈夫曼码 在初始化(I)的过程间,要用输入的字符权值哈夫曼树并求得哈夫曼码。先将输入的字符权值存放到一个结构体数组哈夫曼树,将计算所的哈夫曼码存储到另一个结构体数组。 (2)串的匹配 在码(D)的过程间,要对已经码过的代码译码,可利用循环,将代码的与哈夫曼码的长度相同的串与这个哈夫曼码比较,如果相等就回显并存入文件。 (3)二叉树的遍历 在印哈夫曼树(T)的,因为哈夫曼树也是二叉树,所以就要利用二叉树的先序遍历将哈夫曼树输出。 [测试数据] 根据实验要求,在tobetrans.dat输入"THIS PROGRAM IS MY FAVORITE",字符集和其频度如下: 字符 __ A B C D E F G H I J K L M 频度 186 64 23 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 20 56 19 2 50 51 55 30 10 11 2 21 2
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值