符号表结构体:
struct node
{
// 字符串形式存储的Huffman编码
char code[MAX_CODE_LENGTH];
// 这个字符在文件中出现的次数
long count;
// 在生成Huffman树的时候是否已经被当作叶子节点
int checked;
// 符号
char sym;
// left和right只在生成Huffman树的时候有用
struct node* next,*left,*right;
};
全局变量:
const int BIT_WIDTH_CHAR = 8;
const int END_SYM_FLAG = 200;// 符号的范围是-127~128
int gl_total_node_num = 0;// 符号表的总长度
int gl_source_file_length = 0;// 源文件的总长度
辅助函数:
// 在链表中查找指定的字符
// 参数:符号表
// 参数:字符
// 返回值:找到的节点的指针
struct node* content_char(struct node*,char);
// 在链表中查找指定编码
// 参数:符号表
// 参数:编码
// 返回值:找到的节点的指针
struct node* content_code(struct node* list,const char* ch);
// 根据字符创建一个新的节点
// 参数:字符
// 参数:计数
// 返回值:新节点指针
struct node* create_new_node(char ch,int count);
// 插入新节点到符号表的最前面
// 参数list:目标链表
// 参数new_node:新节点
// 返回值:插入后的链表
struct node* insert_node(struct node *list,struct node *new_node);
// 输出链表
// 参数list:目标链表
void print_list(struct node *list);
// 获取到最小的未被检查的count的节点,返回它的指针,并将其设置为检查过的
// 参数list_addr:链表头
// 返回值:第一个未检查的最少出现次数的结点指针
struct node* get_smallest_node(struct node *list_addr);
压缩:
第一步:建立符号表
在main函数中:获取文件的总长度,用于生成进度条
// 获取文件长度,以实现进度条
fseek(source_file,0,SEEK_END);
gl_source_file_length = ftell(source_file);
fseek(source_file,0,SEEK_SET);
扫描源文件,按字符读取,建立符号表,统计每个字符出现的次数
首先提示进度条,10个'.'为结束
将读取到的字符在已有的符号表中查找,如果已存在就将该节点的count+1,否则就创建新节点并插入到符号表中
统计符号表中总节点数
// 生成符号链表(含频率)
// 参数:源文件
// 参数:目标链表
// 返回值:符号链表
struct node* generate_count(FILE* f,struct node*list)
{
printf("counting");
int count=0;
char ch;
struct node *content_node;
while(fread(&ch,sizeof(char),1,f)==1)
{
// 进度条
count++;
if(count%(gl_source_file_length/10+1)==0)
printf(".");
// 插入符号表
content_node = content_char(list,ch);
if(content_node)
content_node->count++;
else
{
gl_total_node_num++;
list=insert_node(list,create_new_node(ch,1));
}
}
printf("\n");
return list;
}
第二步:生成Huffman树
生成Huffman树
使用先前统计的符号表总数进行计数循环,因为生成树的所有非叶子节点共有叶子节点-1个
首先输出进度条
视初始时所有的符号表中的节点为只有一个节点的子树,获取两个最小的count的子树根节点指针,这是通过节点的checked属性实现的,如果使用了两个子树根节点生成新的子树