哈夫曼压缩原理就是构建二叉树,出现频率高的字母用更少的位数来表示,实现压缩的效果
比如字符串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; //记录左孩子节点地址