c++实现哈夫曼huffman压缩文本

2023-11-05

哈夫曼压缩原理就是构建二叉树,出现频率高的字母用更少的位数来表示,实现压缩的效果

比如字符串abcbbc

构建哈夫曼树

这样构建出编码表b->0,a->10,  c->11

原本6个字符要48位来表示,现在只需要9位来表示即可

 

1.    首先将文本文件的每一个字符进行统计,构建编码表,这个编码表大概50几k

void readTxt(string file,Character *cList)

{

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行



    char c;

    infile >> noskipws;

    while (!infile.eof())

    {

        addList(c,cList);

        infile>>c;

    }

    infile.close();            //关闭文件输入流

}

void addList(char c,Character *cList){

    //为空就退出

    if(!c) return;

    totalNum++;

    int i=0;

    for(;i<cnumber;i++){

        if(c==cList[i].data){

            break;

        }

    }



    cList[i].number++;



    if(i==cnumber){

        cList[cnumber].data = c;

        cnumber++;

    }



    if(i>0 && cList[i].number>cList[i-1].number){

        int num = cList[i].number;

        char ch = cList[i].data;

        cList[i].data = cList[i-1].data;

        cList[i].number = cList[i-1].number;

        cList[i-1].data = ch;

        cList[i-1].number = num;

    }

}

 

1.    根据huffman树原理,构建编码表(如下图所示),词频越高则用更小的编码表示

 

 

//换行符

string enterCode;



typedef struct node{

    char ch;                         //存储该节点表示的字符,只有叶子节点用的到

    int val;                         //记录该节点的权值

    structnode *self,*left,*right;   //三个指针,分别用于记录自己的地址,左孩子的地址和右孩子的地址

    friend booloperator <(const node &a,constnode &b) //运算符重载,定义优先队列的比较结构

    {

        return a.val>b.val;          //这里是权值小的优先出队列

    }

}node;

int codeTablePlace = 0;

node *myroot;

priority_queue<node> p;               //定义优先队列

char res[10000];                         //用于记录哈夫曼编码

void dfs(node *root,int level,CodeTableItem *codetable,Character *cList)        //哈夫曼编码表

{

    if(root->left==root->right)       //叶子节点的左孩子地址一定等于右孩子地址,且一定都为NULL;叶子节点记录有字符

    {

        if(level==0)                  //“AAAAA”这种只有一字符的情况

        {

            res[0]='0';

            level++;

        }

        res[level]='\0';              //字符数组以'\0'结束

        //printf("%c=>%s\n",root->ch,res);

        //搜索在统计表中的位置

        for(int j=0;j<cnumber;j++){

            if(root->ch==cList[j].data){

                codetable[j].data = root->ch;

                codetable[j].code =res;

            }

        }

//        codetable[codeTablePlace].data = root->ch;

//        codetable[codeTablePlace].code = res;

        if(int(root->ch)==10)

            enterCode =res;

       // codeTablePlace++;

    }

    else

    {

        res[level]='0';               //左分支为0

        dfs(root->left,level+1,codetable,cList);

        res[level]='1';               //右分支为1

        dfs(root->right,level+1,codetable,cList);

    }

}

void huffman(Character *hash,CodeTableItem *codeTable)               //构建哈夫曼树

{

    node *root,fir,sec;

    for(int i=0;i<cnumber;i++)

    {

        root=(node *)malloc(sizeof(node));         //开辟节点

        root->self=root;                           //记录自己的地址,方便父节点连接自己

        root->left=root->right=NULL;               //该节点是叶子节点,左右孩子地址均为NULL

        root->ch=hash[i].data;                            //记录该节点表示的字符

        root->val=hash[i].number;                         //记录该字符的权值

        p.push(*root);                             //将该节点压入优先队列

    }

    //下面循环模拟建树过程,每次取出两个最小的节点合并后重新压入队列

    //当队列中剩余节点数量为1时,哈夫曼树构建完成

    while(p.size()>1)

    {

        fir=p.top();p.pop();     //取出最小的节点

        sec=p.top();p.pop();     //取出次小的节点

        root=(node *)malloc(sizeof(node));         //构建新节点,将其作为fir,sec的父节点

        root->self=root;                           //记录自己的地址,方便该节点的父节点连接

        root->left=fir.self;     //记录左孩子节点地址

        root->right=sec.self;    //记录右孩子节点地址

        root->val=fir.val+sec.val;//该节点权值为两孩子权值之和

        p.push(*root);           //将新节点压入队列

    }

    fir=p.top();p.pop();         //弹出哈夫曼树的根节点

    myroot = fir.self;

    dfs(fir.self,0,codeTable,CList);             //输出叶子节点记录的字符和对应的哈夫曼编码

}

 

1.     接下来是压缩的关键部分,首先读入一行的字符,通过编码表将该行加密成01串。根据01串构造一个个字符,一个char型字符是1个字节,也就是8个01就可以通过移位和加一来构建一个char类型,然后将该字符写入文件。这里要注意的是一行最后要加上换行符号的编码,直接读取一行是不会读取到换行符的。其次是一行最后的一个字符一般是没有构建完的,这样就会在最后一个字符的ASCII码的二进制表示前面引入几个0,应该将该字符记录下来,在新的一行构建字符的时候将该字符补全。

 

 

 

 

/*加密文件*/

string tempstr="";

ofstream out;

//记录上一行尾部没有填充完的字符leftChar和还剩多少位填完leftBit;

char leftChar=0;

int leftBit=0;

void encodeLine(string line,string path,CodeTableItem *cTable){

    if (line=="" ) {

        return;

    }

    if (out.is_open())

    {

        //将一行文本加密成二进制字符串

        string str= "";

        for(int i=0;i<line.size();i++){

            for(int j=0;j<cnumber;j++){

                if(line[i]==cTable[j].data){

                    str+=cTable[j].code;

                    break;

                }

            }

        }

        //加上换行符

        str+=enterCode;

        int count = 0;

        unsigned char c=leftChar;

        //先把上一行字符填充完

        for(int i=0;i<leftBit;i++){

            c<<=1;

            if(str[i]=='1'){

                c = c + 1;

            }

        }

        //cout<<str<<"-";

        //tempstr+=c;

        char ch1 = c;

        out<<ch1;

        //加密这一行剩下的

        for(int i=leftBit;i<str.size();i++){

            c<<=1;

            count++;

            if(str[i]=='1'){

                c = c + 1;

            }

//            //一个字节存满,将改字节保存

            if(count==8){

                ch1 = c;

                out<<ch1;

                //tempstr+=c;

                c = 0;

                count=0;

            }

        }

        //记录没有保存的字符

        leftChar = c;

        leftBit = 8-count;

        out<<tempstr;

        //cout<<"1";

    }

}



void encodeHuffman(string file,string path,CodeTableItem *cTable){

    //读取文件,一个个字符加密

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行

    infile >> noskipws;

    string line;

    while (std::getline(infile, line)) {

        encodeLine(line,path,cTable);

    }

    infile.close();

}

 

解密过程也很简单,就是加密的逆过程读取一个字符将其解密成01串,然后顺着01串遍历huffman树,如果是叶子节点就输出字符,并且回到根节点准备下一个字符的解密。

 

//解密

void decodeHuffman(string file){

    node *p = myroot;

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行

    unsigned char c;

    infile >> noskipws;

    int wordcount=0;

    infile>>c;

    while(!infile.eof())

    {

        infile>>c;

        string str = "";

        int beichushu = 128;

        for(int i=0;i<8;i++){

            int bit = (int)c/beichushu;

            if(bit==1){

                p = p->right;

                str+= "1";

            }

            else{

                str+= "0";

                p = p->left;

            }

            if(p->left==NULL){

                //cout<<p->ch;

                out<<p->ch;

                //当执行到最后一个字符就结束,不要继续输出后面多余字符

                wordcount++;

                if(wordcount==totalNum)

                    return;

                p = myroot;

            }

            c <<= 1;

        }

        //cout<<str;

        //cout<<wordcount;

    }

    infile.close();            //关闭文件输入流

}

 

 

 

 

mac上只有zip,所以直接用命令行查看压缩时间。压缩cacm.all压缩时间

 

 

 

我的算法与zip的对比

规模

我的压缩时间

zip时间

我的压缩率

zip压缩率

2.2M

0.9s

0.11s

63.6%

31.8%

22M

14.3s

1.7s

65.3%

35%

220M

148.6s

10.8s

65.5%

32%

2200M

1522.1s

63.2s

65.7

30%

将他们时间取对数后发现,我的代码几乎是线性递增的,我这个文件是有原来的cacm文件多次复制张贴而来,所以构成的哈夫曼树是一样的,压缩率也就差不多一样。而zip就比较神奇,也是预料之中,可能是它出现次数越多的字母的权重越大,是时间在逐渐减少,而且压缩率也降低。

 

 

完整代码

由于忙着赶作业写得有点乱,代码也没有美化和优化,现在闲下来反而不想弄了

#include <iostream>

#include <iostream>

#include <fstream>

#include <cassert>

#include <string>

#include <time.h>

#include<queue>

#include<stdlib.h>

#include<bitset>



using namespace std;



class Character

{

public:

    char data;

    int number;

    

    Character(){

        data = ' ';

        number = 0;

    }

};



class CodeTableItem{

public:

    char data;

    string code;

};



//字符统计表

Character CList[200];

int cnumber=0,totalNum =0;



void addList(char c,Character *cList){

    //为空就退出

    if(!c) return;

    totalNum++;

    int i=0;

    for(;i<cnumber;i++){

        if(c==cList[i].data){

            break;

        }

    }



    cList[i].number++;



    if(i==cnumber){

        cList[cnumber].data = c;

        cnumber++;

    }



    if(i>0 && cList[i].number>cList[i-1].number){

        int num = cList[i].number;

        char ch = cList[i].data;

        cList[i].data = cList[i-1].data;

        cList[i].number = cList[i-1].number;

        cList[i-1].data = ch;

        cList[i-1].number = num;

    }

}



void readTxt(string file,Character *cList)

{

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行



    char c;

    infile >> noskipws;

    while (!infile.eof())

    {

        addList(c,cList);

        infile>>c;

    }

    infile.close();            //关闭文件输入流

}

//换行符

string enterCode;



typedef struct node{

    char ch;                         //存储该节点表示的字符,只有叶子节点用的到

    int val;                         //记录该节点的权值

    structnode *self,*left,*right;   //三个指针,分别用于记录自己的地址,左孩子的地址和右孩子的地址

    friend booloperator <(const node &a,constnode &b) //运算符重载,定义优先队列的比较结构

    {

        return a.val>b.val;          //这里是权值小的优先出队列

    }

}node;

int codeTablePlace = 0;

node *myroot;

priority_queue<node> p;               //定义优先队列

char res[10000];                         //用于记录哈夫曼编码

void dfs(node *root,int level,CodeTableItem *codetable,Character *cList)        //哈夫曼编码表

{

    if(root->left==root->right)       //叶子节点的左孩子地址一定等于右孩子地址,且一定都为NULL;叶子节点记录有字符

    {

        if(level==0)                  //“AAAAA”这种只有一字符的情况

        {

            res[0]='0';

            level++;

        }

        res[level]='\0';              //字符数组以'\0'结束

        //printf("%c=>%s\n",root->ch,res);

        //搜索在统计表中的位置

        for(int j=0;j<cnumber;j++){

            if(root->ch==cList[j].data){

                codetable[j].data = root->ch;

                codetable[j].code =res;

            }

        }

//        codetable[codeTablePlace].data = root->ch;

//        codetable[codeTablePlace].code = res;

        if(int(root->ch)==10)

            enterCode =res;

       // codeTablePlace++;

    }

    else

    {

        res[level]='0';               //左分支为0

        dfs(root->left,level+1,codetable,cList);

        res[level]='1';               //右分支为1

        dfs(root->right,level+1,codetable,cList);

    }

}

void huffman(Character *hash,CodeTableItem *codeTable)               //构建哈夫曼树

{

    node *root,fir,sec;

    for(int i=0;i<cnumber;i++)

    {

        root=(node *)malloc(sizeof(node));         //开辟节点

        root->self=root;                           //记录自己的地址,方便父节点连接自己

        root->left=root->right=NULL;               //该节点是叶子节点,左右孩子地址均为NULL

        root->ch=hash[i].data;                            //记录该节点表示的字符

        root->val=hash[i].number;                         //记录该字符的权值

        p.push(*root);                             //将该节点压入优先队列

    }

    //下面循环模拟建树过程,每次取出两个最小的节点合并后重新压入队列

    //当队列中剩余节点数量为1时,哈夫曼树构建完成

    while(p.size()>1)

    {

        fir=p.top();p.pop();     //取出最小的节点

        sec=p.top();p.pop();     //取出次小的节点

        root=(node *)malloc(sizeof(node));         //构建新节点,将其作为fir,sec的父节点

        root->self=root;                           //记录自己的地址,方便该节点的父节点连接

        root->left=fir.self;     //记录左孩子节点地址

        root->right=sec.self;    //记录右孩子节点地址

        root->val=fir.val+sec.val;//该节点权值为两孩子权值之和

        p.push(*root);           //将新节点压入队列

    }

    fir=p.top();p.pop();         //弹出哈夫曼树的根节点

    myroot = fir.self;

    dfs(fir.self,0,codeTable,CList);             //输出叶子节点记录的字符和对应的哈夫曼编码

}

/*加密文件*/

string tempstr="";

ofstream out;

//记录上一行尾部没有填充完的字符leftChar和还剩多少位填完leftBit;

char leftChar=0;

int leftBit=0;

void encodeLine(string line,string path,CodeTableItem *cTable){

    if (line=="" ) {

        return;

    }

    if (out.is_open())

    {

        //将一行文本加密成二进制字符串

        string str= "";

        for(int i=0;i<line.size();i++){

            for(int j=0;j<cnumber;j++){

                if(line[i]==cTable[j].data){

                    str+=cTable[j].code;

                    break;

                }

            }

        }

        //加上换行符

        str+=enterCode;

        int count = 0;

        unsigned char c=leftChar;

        //先把上一行字符填充完

        for(int i=0;i<leftBit;i++){

            c<<=1;

            if(str[i]=='1'){

                c = c + 1;

            }

        }

        //cout<<str<<"-";

        //tempstr+=c;

        char ch1 = c;

        out<<ch1;

        //加密这一行剩下的

        for(int i=leftBit;i<str.size();i++){

            c<<=1;

            count++;

            if(str[i]=='1'){

                c = c + 1;

            }

//            //一个字节存满,将改字节保存

            if(count==8){

                ch1 = c;

                out<<ch1;

                //tempstr+=c;

                c = 0;

                count=0;

            }

        }

        //记录没有保存的字符

        leftChar = c;

        leftBit = 8-count;

        out<<tempstr;

        //cout<<"1";

    }

}



void encodeHuffman(string file,string path,CodeTableItem *cTable){

    //读取文件,一个个字符加密

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行

    infile >> noskipws;

    string line;

    while (std::getline(infile, line)) {

        encodeLine(line,path,cTable);

    }

    infile.close();

}



//解密

void decodeHuffman(string file){

    node *p = myroot;

    ifstream infile;

    infile.open(file.data());  //将文件流对象与文件连接起来

    assert(infile.is_open());  //若失败,则输出错误消息,并终止程序运行

    unsigned char c;

    infile >> noskipws;

    int wordcount=0;

    infile>>c;

    while(!infile.eof())

    {

        infile>>c;

        string str = "";

        int beichushu = 128;

        for(int i=0;i<8;i++){

            int bit = (int)c/beichushu;

            if(bit==1){

                p = p->right;

                str+= "1";

            }

            else{

                str+= "0";

                p = p->left;

            }

            if(p->left==NULL){

                //cout<<p->ch;

                out<<p->ch;

                //当执行到最后一个字符就结束,不要继续输出后面多余字符

                wordcount++;

                if(wordcount==totalNum)

                    return;

                p = myroot;

            }

            c <<= 1;

        }

        //cout<<str;

        //cout<<wordcount;

    }

    infile.close();            //关闭文件输入流

}



void testWrite(string savePath){

    string str = "";

    for(int i=0;i<1000;i++){

        str+='a';

    }

    ofstream ofile;              //定义输出文件

    ofile.open(savePath);     //作为输出文件打开

    for(int i=0;i<10;i++){

        ofile<<str;

    }

    ofile.close();

}



int main(int argc, const char * argv[]) {

    srand((unsigned)time(NULL));//用时间做种,每次产生随机数不一样

    clock_t start,finish;

    double time;

    start = clock();

    

    string file ="/Users/ish/Documents/code/c++/Huffman/cacm.all";

    string encodeFile="/Users/ish/Documents/code/c++/Huffman/encode.all";

    string decodefile="/Users/ish/Documents/code/c++/Huffman/decode.all";

    //构建统计表

    readTxt(file,CList);

    //动态初始化编码表

    CodeTableItem *codeTable =new CodeTableItem[cnumber];

    //构建树并且建立编码表

    huffman(CList,codeTable);

    //输出编码表

//    for(int i=0;i<cnumber;i++){

//        cout<<codeTable[i].data<<"->"<<codeTable[i].code<<endl;

//    }

    //加密

    out.open(encodeFile);

    encodeHuffman(file, encodeFile,codeTable);

    out.close();

    //解密

    out.open(decodefile);

    decodeHuffman(encodeFile);

    out.close();

    

    finish=clock();

    time = static_cast<double>(finish - start)/CLOCKS_PER_SEC*1000; //ms

    cout<<"运行时间为:"<<time<<"ms"<<endl;

    return 0;

}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++实现哈夫曼huffman压缩文本 的相关文章

  • Kotlin基础

    Kotlin是什么 它是一种针对 java 平台的新编程语言 Kotlin 简洁 安全 务实 Kotlin可以运行在 任何 java 运行的地方 并且能够和 java语言无缝对接上 kotlin语言和java语言一样 都是静态语言 java
  • PCB设计时对于EMC有哪些需要注意的?

    详细如下 EMC的PCB设计技术 分层 布局 布线 pcb的emc设计 Me sl 的博客 CSDN博客 PCB EMC 设计的关键 是尽可能减小回流面积 让回流路径按照设计的方向流动 最常见返回电流问题来自于参考平面的裂缝 变换参考平面层

随机推荐

  • MySQL数据库 【增删改查】

    目录 一 新增 指定列插入 一次插入多个数据 二 查询 1 全列查询 2 指定列查询 3 查询字段为表达式 4 查询的时候给列名 表达式 指定别名 5 查询时去重 6 排序查询 7 条件查询 8 模糊查询 9 空值查询 10 分页查询 三
  • JavaScript中的字符串替换

    今天一大早遇到个状况 json字符串中有些undefined数据 导致图表不能正常显示 本来打算用isNaN判断是否是数字 后来感觉操作起来有些麻烦 就打算用 null 把 undefined 全部替换到 于是用replace函数进行替换
  • 菜鸟的我运行了hello word 在华为鸿蒙2.0beta

    相信自从华为上次华为鸿蒙发布会之后 不少尝鲜用户都已经使用华为鸿蒙的IDE开发程序 那么网上的教程也很多 这里我通过华为鸿蒙官方教程成功安装并且成功运行hello word 我还是菜鸟 大佬勿喷 鸿蒙源码 https openharmony
  • MFC之菜单栏的相关使用14

    1 菜单栏选项的打勾 加粗 禁用 首先我们需要知道菜单栏包含子菜单栏 依次使用下标去区分 然后拿到子菜单栏后 就可以操作里面的选项了 可以通过下标 选项的ID 在资源视图的菜单栏的图 点击选项右击属性即可获取 进行操作 代码 由于为了减少视
  • 【学习日志】【TCN】时间序列卷积神经网络(1)

    1 ask bing Temporal Convolutional Network 问 我对CNN RNN TCN等神经网络没有任何基础 你能直观地给我讲一下TCN的结构 输入输出和原理吗 bing对TCN的解释如下 TCN是一种用于处理序
  • C++标准库之IO库

    IO类 基本内容 iostream库包含两个基础类型 istream ostream cin 一个istream对象 用来从标准输入读取数据 cout 同cin cerr 用于输出程序错误信息 写到标准错误 方法 getline 从一个给定
  • 蓝桥杯大赛获奖选手,可获研究生推免加分啦,挺好的呀

    大家好 我是涛哥 我一直关注着各类大会和各类比赛 之前也写过蓝桥杯大赛的一些攻略 并用实际的题目和案例 为大家准备蓝桥杯比赛提供了指导 蓝桥杯大赛其实并不难 但好处很多 有的朋友可能对蓝桥杯还不太了解 不过没关系 我简单来跟大家说说 希望广
  • Java Double compare()方法具有什么功能呢?

    转自 Java Double compare 方法具有什么功能呢 下文笔者将讲述compare 方法的功能简介说明 如下所示 compare 方法的功能 java lang Double compare 方法的功能 用于比较两个基础类型的d
  • HTML5----响应式(自适应)网页设计(自动适应屏幕大小)

    HTML5 响应式 自适应 网页设计 自动适应屏幕大小 现在 很多项目都需要做响应式或者自适应的来适应我们不同屏幕尺寸的手机 电脑等设备 那么就需要我们在页面上下功夫 但移动端的布局不同于pc端 首先我们要知道在移动端中 css中的1px并
  • MyEclipse设置Java代码注释模板

    定义自己喜欢的模板注释 选中你要加注释的方法或类 按 Alt shift J 文件 Files 注释标签 Title file name Package package name Description todo author yok
  • 抓包工具mitmprox

    安装 我这里是在pycharm下项目setting安装的 设置环境变量 将下面exe这个路径添加至path 启动mitmproxy https blog csdn net shifengboy article details 1140672
  • 北邮22信通:实验五 共射放大电路的频率特性与深负反馈的影响

    北邮22信通一枚 很高兴以一个新身份与大家见面 关注作者 解锁更多邮苑模电实验报告 获取更多文章 请访问专栏 北邮22信通 电子电路 青山如墨雨如画的博客 CSDN博客 目录 实验目的 实验设备及器件 实验内容 1 频率特性分析 1 1 C
  • C# linq初探 使用linq查询数组中元素

    使用linq进行数组查询 输出数组中全部的偶数并升序输出结果 写法1 int numbers 5 10 8 3 6 12 查询的数组 var numqurey from num in numbers where num 2 0 按照条件过滤
  • 区间预测

    区间预测 MATLAB实现SARIMA季节性数据时间序列预测 arima函数 目录 区间预测 MATLAB实现SARIMA季节性数据时间序列预测 arima函数 预测效果 基本介绍 研究回顾 模型结构 程序设计 参考资料 预测效果 基本介绍
  • latex 基本用法(二)—— 矩阵(增广矩阵、长虚线)

    latex 基本用法 modm mod m mod modn pmod n pmod 1 增广矩阵 比如鸡兔同笼问题的线性方程组 x y 152x 4y 40 begin split x y 15 2x 4y 40 end split 首先
  • android 自定义控件--(圆盘形菜单控件)

    思路原理 定一个原点和一个半径 圆的四周均匀分布每个菜单 为了方便计算 菜单的坐标用度数表示 然后转化为极坐标计算 定某个点为起始点 根据总菜单数确定每个点增加的度数 然后依次确定每个点的度数 也就确定了坐标 源代码 package chr
  • linux下C语言修改文件权限

    头文件
  • Java 统计文本文件中字符数量

    设有一个文本文件word01 txt 内容如下 after a minute or two and said to his friend he opened them again a minute or two and said to fr
  • 【数据结构——树】Trie树的两种实现方式:二叉树(左孩子右兄弟)与二十六叉树

    输入 输入的第一行为一个正整数n 表示词典的大小 其后n行 每一行一个单词 不保证是英文单词 也有可能是火星文单词哦 单词由不超过10个的小写英文字母组成 可能存在相同的单词 此时应将其视作不同的单词 接下来的一行为一个正整数m 表示询问的
  • c++实现哈夫曼huffman压缩文本

    哈夫曼压缩原理就是构建二叉树 出现频率高的字母用更少的位数来表示 实现压缩的效果 比如字符串abcbbc 构建哈夫曼树 这样构建出编码表b gt 0 a gt 10 c gt 11 原本6个字符要48位来表示 现在只需要9位来表示即可 1