数据结构——哈夫曼编/译码器

哈夫曼编/译码器

  • 任务:建立最优二叉树函数。
  • 要求:可以建立函数输入二叉树,并输出其哈夫曼树。

在上交资料中请写明:存储结构、基本算法(可以使用程序流程图)、输入输出、源程序、测试数据和结果、算法的时间复杂度、另外可以提出算法的改进方法;

利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。

  • 一个完整的系统应具有以下功能:

(1)I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。

(2)E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。

(3)D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。

(4)P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。

(5)T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。

 

  • 题目不分析了,说一下我的代码

0. 假定你看这篇文章的时候已经掌握哈夫曼树原理

1. 原文两种输入方式:手动输入和随机输入

2. 创建哈夫曼树两种方式:通过输入的字符创建 或者 从文件读取

3. Dict数据结构:以键值对的形式存储编码字符和其对应的权值

4. 哈夫曼树以结构体数组形式存储

5. 哈夫曼编码表以二维字符数组形式存储

6. 凹凸打印哈夫曼树使用递归方法,参考

7. plaintext.txt :原文 ciphertext.txt :密文 DecodingResults.txt :解码结果

注意:哈夫曼数组的零号地址不使用

 

Code,环境codeblocks17 通过

//@ChenYe 2019/2/1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include <windows.h>
#define MAXSIZE 10000
#define green SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY)
#define white SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN)
#define red SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_INTENSITY)
#define yellow SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY)
// 类似green; red; white;的语句为颜色控制语句
const int INF = 0x3f3f3f3f;
const int NINF = 0xc0c0c0c0;
//1061109567 -1061109567	最大最小值

typedef struct
{
    char a;
    int weight;
} Dict; // Dict以键值对得形式存储编码字符和对应的权值
typedef struct
{
    int weight;
    int parent,lchild,rchild;
    char data;
} HTNode,*HuffmanTree;
typedef char **HuffmanCode;

void RandomGenerate(char *a, int n)
{
    // 随机生成字符串
    char all[]="ABCDEFGHIJKLMNOPQRSTUVDictXYZabcdefghijklmnopqrstuvwxyz0123456789";
    srand(time(0));
    for (int i=0; i<n; i++)
    {
        a[i]=all[rand()%62];
    }
    a[n]='\0';
    green;
    printf("The plain is: %s\n",a);
    white;
    FILE *fp;
    fp=fopen("plaintext.txt","w");
    fprintf(fp,"%s",a);
    fclose(fp);
    printf("the plain already save in plaintext.txt\n");
}

void ManuallyInput(char a[],int n)
{
    // 手动输入n个字符
    printf("plase input the plain:\n");
    for(int i=0; i<n; i++)  // 输入控制字符数
    {
        scanf("%c",&a[i]);
    }
    fflush(stdin);
    a[n] = '\0';
    green;
    printf("The plain is: %s\n",a);	// 回显
    white;
    FILE *fp;
    fp=fopen("plaintext.txt","w");	// 保存到文件
    fprintf(fp,"%s",a);
    fclose(fp);
    printf("the plain already save in plaintext.txt\n");
}

void Weight(char *a,Dict w[],int &n)
{	// a 是待加密文本
    // Dict 以键值对得形式存储编码字符和对应的权值
	// 求得权值后n改为Huffman树中编码字符的个数(即没有重复出现的字符)
    for(int j=0; j<=n; j++) // 初始化
        w[j].weight=0;
    char *p;
    p=a;
    int i=0,k;
    w[i].a=*p;
    w[i].weight++;
    p++;
    while(*p!='\0')
    {// 每次循环都遍历w,对当前p所指的字符进行处理
	 // 若果在w中就权值加一,否则加入加入w
        for(k=0; k<=i; k++)
        {
            if(w[k].a==*p)	// 在w中出现
            {
                w[k].weight++;
                break;
            }
        }
        if(k>i)	// 不在w中
        {
            i++;
            w[i].a=*p;
            w[i].weight++;
        }
        p++;
    }
    n=i+1;
    for(int x=n; x>0; x--) // w[0]空出来
    {
        w[x] = w[x-1];
    }
    printf("编码字符及其权值:\n");
    for(i=1; i<=n; i++)  // 打印权值
        printf("%c",w[i].a);
    printf("\n");
    for(i=1; i<=n; i++)
        printf("%d",w[i].weight);
    printf("\n");
}

void Select(HuffmanTree HT,int n,int &s1,int &s2)
{
    //在HuffmanTree中筛选权值最小的两个节点下标赋值给s1、s2
    int i=1;
    int min1=INF,min2=INF;//INF = 1061109567
    s1=s2=0;
    while(i<=n)   // min1<min2<others
    {
        if(HT[i].parent==0)
        {
            if(min1>HT[i].weight)
            {
                min2=min1;
                s2=s1;
                min1=HT[i].weight;
                s1=i;
            }
            else if(min2>HT[i].weight)
            {
                s2=i;
                min2=HT[i].weight;
            }
        }// <end if>
        i++;
    }// <end while>
}

void CreateHuffman(HuffmanTree &HT,Dict w[],int n)
{
    // 创建哈弗曼树并存储在HuffmanTree文件中,n为长度
    int i,s1,s2;
    int m=2*n-1;
    if(n<=1)
        return ;
    HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));
    for(i=0; i<=n; i++)
    {
        HT[i].weight=w[i].weight;
        HT[i].data=w[i].a;
        HT[i].lchild=0;
        HT[i].parent=0;
        HT[i].rchild=0;
    }
    for(; i<=m; ++i)
    {
        HT[i].weight=0;
        HT[i].lchild=0;
        HT[i].parent=0;
        HT[i].rchild=0;
    }
    for(i=n+1; i<=m; i++) //建立哈夫曼树
    {
        s1=s2=0;
        Select(HT,i-1,s1,s2);
        HT[s1].parent=i;
        HT[s2].parent=i;
        HT[i].lchild=s1;
        HT[i].rchild=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;
    }
    // 写入文件
    FILE *fp = fopen("HuffmanTree","wb");
    fwrite((HuffmanTree)&HT, sizeof(HTNode), m+1, fp);
    fclose(fp);

}

void Huffman_CodeTable(HuffmanTree HT,HuffmanCode &HC,int n)
{//得到并打印哈夫曼树的编码表
   //求出n个字符的哈夫曼编码HC
    if(n<=1)
        return ;
    printf("编码表:\n");
    int i;
    HC = (HuffmanCode)malloc((n+1)*sizeof(char*));
    char *cd=(char*)malloc(n*sizeof(char));
    cd[n-1]='\0';
    for(i=1; i<=n; ++i)//倒序求编码字符的哈夫曼编码
    {
        int start=n-1;
        for(int c=i,f=HT[i].parent; f!=0; c=f,f=HT[f].parent)
            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);
    }
    for(i=1; i<=n; i++)
    {
        printf("%c %d的编码为: %s\n",HT[i].data,HT[i].weight,HC[i]);
    }
    free(cd);
}

void HuffmanCoding(HuffmanTree HT,HuffmanCode HC,int n,char a[])
{
    FILE *fp;
    fp=fopen("ciphertext.txt","w");   // 向文件中写入编码后的字符串
    green;
    printf("\nThe cipher code is:\n");
    int len = strlen(a);
    for(int i=0; i<len; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(a[i]==HT[j].data)
            {
                fprintf(fp,"%s",HC[j]);
                printf("%s",HC[j]);
            }
        }
    }
    white;
    printf("\nthe cipher code already save in ciphertext.txt\n");
    fclose(fp);
}

void HuffmanDecode(HuffmanTree HT,int n)
{
    // Decode函数 从ciphertext.txt读取编码字符串
    char decode[50*MAXSIZE];    // 最大加密字符个数MAXSIZE deocde要接收已加密的01字符串,所以空间要大,否则容易溢出
    int i,len,p;
    FILE *fp,*fp1;
    fp=fopen("ciphertext.txt","r");
    fscanf(fp,"%s",decode);
    fclose(fp);
    len=strlen(decode);
    p=2*n-1;
    green;
    printf("Decode Results is:\n");
    fp1=fopen("DecodingResults.txt","w");
    for(i=0; i<=len; i++)   // 解码过程
    {
        if(HT[p].lchild==0&&HT[p].rchild==0)
        {
            printf("%c",HT[p].data);  // 向屏幕输出
            fprintf(fp1,"%c",HT[p].data); // 向文件输出(保存)
            p=2*n-1;
        }
        if(decode[i]=='0')
            p=HT[p].lchild;
        else if(decode[i]=='1')
            p=HT[p].rchild;
    }
    fclose(fp1);
    white;
}

void ReadHuffman_FromFile(HuffmanTree &HT, int &n)
{
    // 读取HuffmanTree文件中的哈夫曼树,n被修改为其中编码的字符个数
    printf("输入HuffmanTree文件中编码字符的个数:");
    scanf("%d",&n);
    fflush(stdin);
    FILE *fp = fopen("HuffmanTree","rb");
    fread((HuffmanTree)&HT, sizeof(HTNode), 2*n, fp);   // 因为huffman树是以二进制的形式存入文件的,
                                                        //所以读取的时候需要指定读取步长和总长度
    fclose(fp);
}

int DEPTH=0;//递归层次
void PreOrderTraverse(HuffmanTree HT,int k)
{
    //中序遍历并打印树,结果存放于fp中,k为根结点的下标
    int i;

    if(HT[k].rchild!=0)
    {
        DEPTH++;
        PreOrderTraverse(HT,HT[k].rchild);
        DEPTH--;
    }

    for(i=0; i<=DEPTH; i++)
        printf("   ");
    printf("%d\n",HT[k].weight);

    if(HT[k].lchild!=0)
    {
        DEPTH++;
        PreOrderTraverse(HT,HT[k].lchild);
        DEPTH--;
    }
    if(HT[k].lchild==0&&HT[k].rchild==0)
        return;
    return;
} //PreOrderTraverse
void TreePrinting(HuffmanTree HT,int n)
{
    //打印树HT,n为树的叶子结点数
    // 可以修改PreOrderTraverse函数,打印的凹凸哈夫曼树输出到文件
    printf("凹凸哈夫曼树:\n");
    PreOrderTraverse(HT,2*n-1);
    return;
}//TreePrinting


void start(int choice)
{
    int n;
    char ch;
    char a[MAXSIZE];    // 输入字符串
    Dict w[MAXSIZE];    // 键值对形式存储字符串:字符--权值
    HuffmanTree Ht;     // 哈夫曼树数组
    HuffmanCode Hc;     // 哈夫曼编码,二维指针

    DEPTH = 0;

    if(choice == 0)
    {
        printf("输入待加密字符个数:");
        scanf("%d",&n);fflush(stdin);
        RandomGenerate(a,n);
        Weight(a,w,n);  // 求得权值后n改为Huffman树中编码字符的个数(即没有重复出现的字符)
        // 创建哈夫曼树
        CreateHuffman(Ht,w,n);
        // 打印编码表
        Huffman_CodeTable(Ht,Hc,n);
        // 打印凹凸哈夫曼
        TreePrinting(Ht,n);
        // 编码
        HuffmanCoding(Ht,Hc,n,a);
        // 解码
        printf("是否要继续进行解码操作?(y/n)\n");
        scanf("%c",&ch);fflush(stdin);
        if(ch=='y'||ch=='Y')
            ;
        else
            return;
        HuffmanDecode(Ht,n);

        printf("\n");
        system("pause");
        system("cls");

    }
    else if(choice == 1)
    {
        printf("输入字符个数:");
        scanf("%d",&n);
        fflush(stdin);
        ManuallyInput(a,n);
        Weight(a,w,n);

        // 创建
        CreateHuffman(Ht,w,n);
        // 打印编码表
        Huffman_CodeTable(Ht,Hc,n);
        // 打印凹凸哈夫曼
        TreePrinting(Ht,n);
        // 编码
        HuffmanCoding(Ht,Hc,n,a);
        // 解码
        printf("是否要继续进行解码操作?(y/n)\n");
        scanf("%c",&ch);fflush(stdin);
        if(ch=='y'||ch=='Y')
            ;
        else
            return;
        HuffmanDecode(Ht,n);

        printf("\n");
        system("pause");
        system("cls");
    }
    else if(choice == 2)
    {
        ReadHuffman_FromFile(Ht,n); // n出函数后就被修改huffman树编码的字符个数
        yellow; printf("read file down!\n"); white;

        //打印编码表和凹凸哈夫曼树
        Huffman_CodeTable(Ht,Hc,n);
        TreePrinting(Ht,n);
        // 加密
        printf("是否要继续进行加密操作?(y/n)\n");
        scanf("%c",&ch);fflush(stdin);
        if(ch=='y'||ch=='Y')
            ;
        else
            return;
        printf("输入要加密的字符个数:");
        int n2; // n2 表示要输入的原文字符的个数
        scanf("%d",&n2);fflush(stdin);
        ManuallyInput(a,n2);
        HuffmanCoding(Ht,Hc,n,a);
        // 解码
        printf("是否要继续进行解码操作?(y/n)\n");
        scanf("%c",&ch);fflush(stdin);
        if(ch=='y'||ch=='Y')
            ;
        else
            return;
        HuffmanDecode(Ht,n);

        printf("\n");
        system("pause");
        system("cls");
    }
    else
    {
        red;
        printf("error input!\n");
        white;
        system("pause");
        system("cls");
        return;
    }
}

int main()
{
    int choice=0;
    while(1)
    {
        system("cls");
        green;
        printf("##::::'##:'##::::'##:'########:'########:'##::::'##:'##::::'##::::'###::::'##::: ##:\n##:::: ##: ##:::: ##: ##.....:: ##.....:: ##:::: ##: ###::'###:::'## ##::: ###:: ##:\n##:::: ##: ##:::: ##: ##::::::: ##::::::: ##:::: ##: ####'####::'##:. ##:: ####: ##:\n#########: ##:::: ##: ######::: ######::: ##:::: ##: ## ### ##:'##:::. ##: ## ## ##:\n##.... ##: ##:::: ##: ##...:::: ##...:::: ##:::: ##: ##. #: ##: #########: ##. ####:\n##:::: ##: ##:::: ##: ##::::::: ##::::::: ##:::: ##: ##:.:: ##: ##.... ##: ##:. ###:\n##:::: ##:. #######:: ##::::::: ##:::::::. #######:: ##:::: ##: ##:::: ##: ##::. ##:\n..:::::..:::.......:::..::::::::..:::::::::.......:::..:::::..::..:::::..::..::::..::");
        white;
        printf("\nplease input your choice \n0:Randomly generated\n1:Manually Enter\n2:Read FuffmanTree\ntips:强烈建议先通过0/1得到huffman文件再进行读取哈夫曼树的操作\n");
        scanf("%d",&choice);
        system("cls");
        start(choice);
    }
}

/*
test data
根据程序提示
*/

  • 11
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值