Huffman树实现与应用(编码解码压缩解压缩)

在目录中放一个"in.txt"文件,里边放一篇英语文章,用来统计各个字符出现的频率,从而建树。

哈夫曼树头文件:

#ifndef HUFFMAN_H_
#define HUFFMAN_H_
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;

void SelectMin(int weight[],int &amount,int &x,int &y);
void Reverse(char code[],int len);

struct HNode
{
    int weight;
    int parent;
    int lchild;
    int rchild;
};

struct HCode
{
    char data;
    char code[100];
};

class Huffman
{
public:
    void CreatHTree(int weight[],int n);//创建哈夫曼树
    void CreatCodeTable(char zifu[],int n);//用哈夫曼树建字符表
    void displayTable(int n);//显示字符表
    void Encode(char *s,char *d,int n);//编码
    void Decode(char *s,char *d,int n);//解码
    void compress(char *d);//压缩
    void uncompress(char outname[],char result[]);//解压缩
    ~Huffman();
private:
    HNode *HTree;
    HCode *HCodeTable;
};

//根据数组初始化哈夫曼树
void Huffman::CreatHTree(int weight[],int n)
{
    //哈夫曼树的静态三叉链表表示 用-1初始化
    HTree = new HNode[2*n-1];
    for(int i=0; i<n; i++)
    {
        HTree[i].weight=weight[i];
        HTree[i].lchild=-1;
        HTree[i].rchild=-1;
        HTree[i].parent=-1;
    }

    int x=0,y=0;
    //x,y  为数组中最小两个元素的索引
    int amount=n;
    for(int i=n; i<2*n-1; i++)
    {
        SelectMin(weight,amount,x,y);
        HTree[x].parent=HTree[y].parent=i;
        HTree[i].weight=HTree[x].weight+HTree[y].weight;
        HTree[i].lchild=x;
        HTree[i].rchild=y;
        HTree[i].parent=-1;
    }
}

void Huffman::CreatCodeTable(char zifu[],int n)
{
    HCodeTable = new HCode[n];
    for(int i=0; i<n; i++)
    {
        HCodeTable[i].data=zifu[i];
        int child =i;
        int parent=HTree[i].parent;
        int k=0;
        while(parent!=-1)
        {
            if(child==HTree[parent].lchild)
                HCodeTable[i].code[k]='0';//左孩子标'0'
            else
                HCodeTable[i].code[k]='1';//右孩子标'1'0
            k++;
            child = parent;
            parent = HTree[parent].parent;
        }
        HCodeTable[i].code[k]='\0';
        //将编码字符逆置
        Reverse(HCodeTable[i].code,k);
    }
   // cout<<endl<<"Creat HcodeTable successfully!"<<endl;
}

void Huffman::displayTable(int n)
{
    cout<<"zifu    "<<"code"<<endl;

    for(int i=0; i<n; i++)
    {
        cout<<left<<setw(8)<<HCodeTable[i].data<<HCodeTable[i].code<<endl;
    }
}

void Huffman::Encode(char *s,char *d,int n)
{
    int i,j;
    for(j=0; j<(int)strlen(s); j++)
    {
        for(i=0; i<n; i++)
        {
            if(s[j]==HCodeTable[i].data)
            {
                strcat(d,HCodeTable[i].code);
            }
        }
    }
   // cout<<d<<endl;
}

void Huffman::Decode(char *s,char *d,int n)
{
   // char d[99999]= {0};
    int i=0;
    while(*s !=0)
    {
        int parent=2*n-1-1;//根结点在HTree中的下标
        while(HTree[parent].lchild!=-1)
        {
            if(*s =='0')
                parent=HTree[parent].lchild;
            else
                parent=HTree[parent].rchild;
            s++;
        }
        d[i]=HCodeTable[parent].data;
        i++;
    }
}

Huffman::~Huffman()
{
    //delete [] HCodeTable;
    delete [] HTree;
}
//每次选择两个最小的元素,返回其索引,然后求和重新放入数组中
void SelectMin(int weight[],int &amount,int &x,int &y)
{
    int i;
    int sum=0;
    int flag=0;
    while(flag<2)
    {
        //找到数组中最小的元素,并记录其索引
        int min=99999;
        int index=99999;
        for(i=0; i<amount; i++)
        {
            if(min>weight[i] && weight[i]!=-1)
            {
                min=weight[i];
                index=i;
            }
        }
        sum+=min;
        if(flag==0)
        {
            x=index;
        }
        if(flag==1)
        {
            y=index;
        }
        weight[index]=-1;
        flag++;
    }
//把和放回到数组中去
    weight[amount++]=sum;
}

void Reverse(char code[],int len)
{
    char temp[100]= {0};
    int j=0;
    int i;
    for(i=len-1; i>=0; i--,j++)
    {
        temp[j]=code[i];
    }
    for(i=0; i<len; i++)
    {
        code[i]=temp[i];
    }
}

void Huffman::compress(char *d)
{
    int len=strlen(d);
    //若不是8位则补全
    while(len%8)
    {
        d[len]='0';
        len++;
    }
    char storage[10000]= {0};
    int i=0;
    char value;
    for(i=0; i<len; i++)
        if(d[i]=='1')
        {
            value=0b10000000>>(i&0b00000111);
            storage[i>>3]|=value;
        }
    //printf("%x",storage[1]);
    cout<<"压缩后存放的路径:"<<endl;
    char press_destination[100]= {0};
    cin.getline(press_destination,100);
    FILE *fout=fopen(press_destination, "w");
    {
        fwrite(storage, len/8, 1, fout);
    }
    fclose(fout);
}

void Huffman::uncompress(char outname[],char result[])
{
    FILE *uncompress=fopen(outname,"rb");
    char storage[9999]= {0};
    int n=0;
    while(fscanf(uncompress,"%c",&storage[n])==1)
    {
        n++;
    }
    //system("pause");
    fclose(uncompress);
    int i,j,k=0;
    char value;
    for(i=0; i<n; i++)
    {
        for(j=0; j<8; j++)
        {
            value=0b10000000>>(j&0b00000111);
            if((storage[i] & value)!=0) result[k]='1';
            else result[k]='0';
            k++;
        }
    }
}
#endif

哈夫曼树应用:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <conio.h>
#include "Huffman.h"
using namespace std;

void menu()
{

    system("cls");
    system("color 5f");
    cout<<"Huffman树的建立与编码"<<endl<<endl;
    cout<<"1.输出哈夫曼表"<<endl<<endl;
    cout<<"2.编码"<<endl<<endl;
    cout<<"3.解码"<<endl<<endl;
    cout<<"4.压缩"<<endl<<endl;
    cout<<"5.解压缩"<<endl<<endl;
    //cout<<"h 帮助"<<endl<<endl;
    cout<<"e 退出系统"<<endl;
}
struct character
{
    char ch;
    int num;
} list[256],ctemp;
int main()
{
    Huffman hehe;
    int weight[300]= {0};
    char zifu[256]= {0};
    char temp;
    int count=0;
    int i,j;
    //读文件
    FILE *fin=fopen("in.txt", "r");
    while(1)
    {
        fscanf(fin,"%c",&temp);
        if(feof(fin))  break;
        for(i=0; i<count; i++)
        {
            //表中有该字符,则数量加一
            if(temp==list[i].ch)
            {
                list[i].num++;
                break;
            }
        }
        //表中没有该字符,则新增一个结构体来存
        if(i==count)
        {
            list[count].ch=temp;
            list[count++].num++;
        }
    }
    fclose(fin);
    //排序
    for(i=0; i<count-1; i++)
    {
        for(j=i+1; j<count; j++)
        {
            if(list[j].num < list[i].num)
            {
                ctemp=list[j];
                list[j]=list[i];
                list[i]=ctemp;
            }
        }
    }

    for(i=0; i<count; i++)
    {
        zifu[i]=list[i].ch;
        weight[i]=list[i].num;
    }
    hehe.CreatHTree(weight,count);
    hehe.CreatCodeTable(zifu,count);
    int choice;
    char source[999999]= {0};
    char destination[999999]= {0};
    while(1)
    {
        menu();
        switch(choice=getch())
        {
        case '1':
        {
            system("cls");
            hehe.displayTable(count);
            system("pause");
            break;
        }
        case '2':
        {
            system("cls");
            memset(source,0,sizeof(char)*strlen(source));
            memset(destination,0,sizeof(char)*strlen(destination));
            cout<<"请输入要编码的字符:"<<endl;
            cin.getline(source,99999);
            cout<<"编码结果:"<<endl;
            hehe.Encode(source,destination,count);
            cout<<destination<<endl;
            system("pause");
            break;
        }
        case '3':
        {
            system("cls");
            memset(source,0,sizeof(char)*strlen(source));
            memset(destination,0,sizeof(char)*strlen(destination));
            cout<<"请输入要解码的字符:"<<endl;
            cin>>source;
            hehe.Decode(source,destination,count);
            cout<<"解码结果: "<<endl;
            cout<<destination<<endl;
            system("pause");
            break;
        }
        case '4':
        {
            system("cls");
            //初始化清零
            memset(source,0,sizeof(char)*strlen(source));
            memset(destination,0,sizeof(char)*strlen(destination));
            cout<<"请输入要压缩的文本文件的路径:"<<endl;
            char press_source[100]= {0};
            cin.getline(press_source,100);
            FILE *psfin=fopen(press_source,"r");
            int n=0;
            while(!feof(psfin))
            {
                fscanf(psfin,"%c",&source[n]);
                n++;
            }
            fclose(psfin);
            hehe.Encode(source,destination,count);
            hehe.compress(destination);
            cout<<"压缩成功!"<<endl;
            system("pause");
            break;
        }
        case '5':
        {
            system("cls");
            //初始化清零
            memset(source,0,sizeof(char)*strlen(source));
            memset(destination,0,sizeof(char)*strlen(destination));
            cout<<"请输入要解压缩的文本文件的路径:"<<endl;
            char uncompress_source[100]= {0};
            cin.getline(uncompress_source,100);
            hehe.uncompress(uncompress_source,source);
            hehe.Decode(source,destination,count);

           // cout<<destination;

            cout<<"请输入要存储的文本文件的路径:"<<endl;
            char storename[100];
            cin.getline(storename,100);
            FILE *stofin=fopen(storename, "w");
            for(int i=0;i<(int)strlen(destination);i++)
            {
                fprintf(stofin,"%c",destination[i]);
            }
            fclose(stofin);
            system("pause");
            break;
        }
       /* case 'h':
        {
            system("cls");
            system("pause");
            break;
        }*/
        case 'e':
        {
            system("cls");
            cout<<"感谢使用!"<<endl;
            system("pause");
            goto end;
            break;
        }

        default:
        {
            system("cls");
            cout<<"没有这个选项,请重新输入!"<<endl;
            system("pause");
            break;
        }
        }
    }
    return 0;

end:
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值