@[TOC] C语言哈夫曼编码
哈夫曼编码 C 实现
思考
- 之前做过一个编码,解码的,都是先有一个密码本。
- 编码:输入明文的字符串根据码本编码,
- 解码:输入密文的密码,跟局码本转换为明文的字符串。
- 所以第一件事应该是构造一个密码本。
- 构造码本,码本的存储应该用[哈夫曼树],不然就不叫哈夫曼编码了。哈夫曼树的构造:参考《大话数据结构》的步骤
树节点的结构体,首先要能存放 字符,要有权值,树要使用孩子表示法,所以需要左右孩子的指针,既然是码本,还要存放字符对应的编码。要定义一个这样的结构体。
代码过程:
- 控制台输入一串字符,读取,并统计不同字符出现的次数(作为权重)。以小写英文字母为例子(alpha:a~z ascii 97 ~122)
- 用1的字符串构造哈夫曼树,完成密码本
- 根据密码本对输入的字符串做编解码
统计控制台输入字符,然后构造一个码本
//哈夫曼树节点
typedef struct huffNode{
char value;
int weight;
//huffNode *par;
huffNode *lchild;
huffNode *rchild;
char *code;
}huffNode, *huffList;
//读取数据并且统计权值
huffList readStr(int *num)
{ //假设只是输入小写字符
//读取字符串以#结尾
char tmp;
int i = 0;
int weight[26];
huffList List = NULL;
for(; i < 26; i++ )
{
weight[i] = 0;
}
printf("输入数据创建码本\n");
scanf("%c",&tmp);
while(tmp != '#')
{
weight[tmp - 97] += 1;
scanf("%c",&tmp);
//(*num)++;
}
for(i = 0;i<26;i++)
{
if(weight[i] != 0)
(*num)++;
}
List = (huffList )malloc(sizeof(huffNode) * (*num));
for(i = 0;i < 26;i++)
{
if(weight[i] != 0)
{
List[i].value = 97 + i;
List[i].weight = weight[i];
List[i].lchild = NULL;
List[i].rchild = NULL;
List[i].code = "\0";
}
}
return List;
}
//构建霍夫曼树,参考大话数据结构的步骤
huffList creatTree(huffList List,int len)
{
int i = 0,j = 0,newlen = len;
huffNode *newTree = NULL;
sortTree( List, len); //使用插入排序算法
for(j = 1; j < len; j++)
{
huffNode *lfTree = allocNode();
huffNode *rtTree = allocNode();
newTree = allocNode();
cover(lfTree,&List[j]);
cover(rtTree,&List[j-1]);
(*newTree).weight = (*lfTree).weight + (*rtTree).weight;
(*newTree).lchild = lfTree;
(*newTree).rchild = rtTree;
cover(&List[j],newTree);
newlen--;
sortTree(&List[j],newlen);
}
return newTree;
}
//完成码本 对树总的字符编码:树的左边路径 + 0 ,右边路径 + 1;
void huffCode(huffList List)
{
//int test = 0;
if(List->lchild || List->rchild)
{
if(List->lchild)
{
List->lchild->code = (char *)malloc(sizeof(char) * strlen(List->code) + 1);
strcpy(List->lchild->code,List->code);
strcat(List->lchild->code,"0");
}
if(List->rchild)
{
List->rchild->code = (char *)malloc(sizeof(char) * strlen(List->code) + 1);
strcpy(List->rchild->code,List->code);
strcat(List->rchild->code,"1");
}
if(List->lchild)
huffCode(List->lchild);
if(List->rchild)
huffCode(List->rchild);
}
}
利用构造好的码本进行编解码
void Encode(huffList List)
{
char tmp;
char arr[MAX_SIZE];
codeBook *cb;
int i = 0,j = 0;
int strLen = 0;
int cbLen = 0;
huffList ptr = List;
huffQuePtr hptr;
hptr.front=hptr.rear = NULL;
codeBook tmpCode;
char *aftercode = "\0";
char *chPtr = NULL;
//用数组存输入数据
char *str = "\0";
fflush(stdin);
printf("输入待编码的字符串\n");
scanf("%c",&tmp);
while(tmp != '#' && i < 255)
{
arr[strLen] = tmp;
scanf("%c",&tmp);
strLen++;
}
cb = (codeBook *)malloc(sizeof(codeBook)*strLen);
//需要二叉树的层序遍历
//越靠近上层的节点出现的概率越高理论上这样编码是快的,但是本场景下,如果使用查找数组的方式编码 时间复杂度会是O(1)
//解码感觉程序遍历 哈夫曼树,每个叶子节点的 code 作为字串用 kmp 算法匹配 代解码字符串这样会快一些。
// 编码需要层序遍历,参考:https://blog.csdn.net/m0_37925202/article/details/80796010
//用数组存码本,index 0 ~ 100 ,权重高->权重低
queuePush(&hptr,*List);
while(!isEmpty(hptr))
{
if(hptr.front->node.value != 0)
{
tmpCode.val = hptr.front->node.value;
tmpCode.code=hptr.front->node.code;
memcpy(&cb[cbLen],&tmpCode,sizeof(codeBook));
cbLen++;
}
if(hptr.front->node.lchild != NULL)
{
queuePush(&hptr,*(hptr.front->node.lchild));
}
if(hptr.front->node.lchild != NULL)
{
queuePush(&hptr,*(hptr.front->node.rchild));
}
//已经操作过的可以出列。
popQ(&hptr);
}
//编码
for(i = 0;i<strLen;i++)
{
while(j<cbLen)
{
if(arr[i] == cb[j].val)
{
chPtr = (char *)malloc(sizeof(char) * (strlen(aftercode) + strlen(cb[j].code)));
strcpy(chPtr,aftercode);
aftercode = chPtr;
strcat(aftercode,cb[j].code);
break;
}
j++;
}
if(j==cbLen)
{
printf("字符 %c 不在码本中\n",arr[i]);
}
j = 0;
}
printf("编码之后: %s\n",aftercode);
//解码
printf("解码 :");
while(strlen(aftercode))
{
if(*(aftercode)== '0')
{
ptr = ptr->lchild;
if(ptr->value != 0)
{
printf("%c",ptr->value);
ptr = List;
}
}
else
{
ptr = ptr->rchild;
if(ptr->value != 0)
{
printf("%c",ptr->value);
ptr = List;
}
}
aftercode++;
}
printf("hello \n");
}
代码下载:
https://download.csdn.net/download/weixin_42592516/20632220