Huffman树:
给定N个权值作为N个叶子节点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
在计算机数据处理中,哈夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
具体的逻辑在这里就不多说了,网上一大堆...这里主要说一说我的代码思路。
结构体定义:
{ *l, data, *r}: 该结构体的data记录字符,l 指向左节点,r 指向右节点,在建立Huffman树的时候要用到。
{*Hnode, cnt, *next}: 该结构体的 Hnode 指向 {*l, data, *r} 结构体,cnt 记录字符的权重,*next 指向 {*Hnode, cnt, *next} 结构体,插入链表和排序的时候需要用到。
{size ,*first}: 该结构体的size记录链表的长度,*first 是一个指向结构体HuffTree的指针。
代码思路:
输入一行字符串,记录一下每个字符出现的次数也就是这个字符的权重,然后用链表将它们按顺序连接 (如下图)。
假设输入的字符串为:abcda
每次取出最小的两个结点,小的放左边,大的放右边,构建一个新的结点,这个新结点的权重是刚才取出来的两个结点权重的和。
再将这个新结点插入链表,依旧按顺序排放,表头的 size--。
如此往复,最终链表里只剩下一个结点,此时这个结点就是Huffman树的根节点。
遍历这棵树,拿到每个字符的编码。注:使用栈记录走过的路。
例:想要获取字符 a 的编码,从根节点往左走 (往左走记为0,往右走记为1),则把0压入栈,然后判断该结点有没有左右孩子 (通过观察发现,存入字符的点都没有左右孩子),如果有,那栈内存放的路径就是该字符所对应的编码。a: 0, b: 110, c: 111, d: 10。
所以字符串 abcda 对应的编码为: 0110111100
遍历Huffman编码,0为往左走,1为往右走,找到字符后存入数组,返回根结点,继续找下一个字符,直到遍历完所有的编码,此时数组内存放的就是解码后的字符串。
具体实现:
statement.h // 自定义一个头文件
#ifndef _INIT_STATEMENT_H
#define _INIT_STATEMENT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 256
typedef char ElemType;
typedef int Count;
// 霍夫曼树结点
typedef struct HuffNode
{
ElemType data;
struct HuffTree *left, *right;
} HuffNode;
// 霍夫曼树
typedef struct HuffTree
{
HuffNode *Hnode;
Count cnt;
struct HuffTree *next;
} HuffTree;
// 链表
typedef struct LinkList
{
int size;
HuffTree *first;
} LinkList;
typedef struct Stack
{
ElemType *base, *top;
int STACK_SIZE;
} Stack;
ElemType code[MAX_SIZE];
ElemType resStr[MAX_SIZE];
ElemType codeTable[MAX_SIZE][MAX_SIZE];
// HuffTree.c
HuffTree *getPriority(ElemType *str);
HuffTree *LinkNode(ElemType *array);
void sortNode(LinkList *list, HuffNode *newNode, int cnt);
HuffTree *createHuffTree(LinkList *list);
void insertNode(LinkList *list, HuffTree *cnn);
// Encode.c
ElemType *encode(HuffTree *root, ElemType *str);
void createStack(Stack *stack);
void recurse(HuffTree *root, Stack *stack);
ElemType *splice(ElemType *code, ElemType *str);
void destoryStack(Stack *stack);
// Decode.c
ElemType *decode(HuffTree *root, ElemType *code);
void destroyHuffTree(HuffTree *root);
#endif
mainPort.c
#include "statement.h"
int main(void)
{
char s[100] = "How are you?"; // 在这输入想要编码的字符串(数字、字母或符号)
printf("string: %s\n", s);
HuffTree *tree = BuildHuffmanTree(s);
ElemType *code = encode(tree, s);
printf("encode: %s\n", code);
ElemType *res = decode(tree, code);
printf("decode: %s\n", res);
return 0;
}
HuffTree.c
#include "statement.h"
// 计数
HuffTree *BuildHuffmanTree(ElemType *str)
{
ElemType array[MAX_SIZE] = {0};
for (int i = 0; i < strlen(str); i++)
{
array[str[i]] ++;
}
return LinkNode(array);
}
// 创建结点, 并连接这些结点
HuffTree *LinkNode(ElemType *array)
{
static LinkList list;
list.size = 0;
for (int i = 0; i < MAX_SIZE; i++)
{
if ( 0 != array[i] )
{
HuffNode *newNode = (HuffNode *)malloc(sizeof(HuffNode));
newNode->data = (char)i;
newNode->left = NULL;
newNode->right = NULL;
sortNode(&list, newNode, array[i]);
}
}
return createHuffTree(&list);
}
// 给结点排序(从小到大)
void sortNode(LinkList *list, HuffNode *newNode, int cnt)
{
if ( list->size > MAX_SIZE )
{
printf(" out of size !\n");
exit(0);
}
HuffTree *listNode = (HuffTree *)malloc(sizeof(HuffTree));
listNode->Hnode = newNode;
listNode->cnt = cnt;
listNode->next = NULL;
if ( 0 == list->size )
{
list->first = listNode;
list->size ++;
}
else
{
if (list->first->cnt >= cnt)
{
listNode->next = list->first;
list->first = listNode;
list->size ++;
return;
}
HuffTree *temp = list->first;
while ( temp->next )
{
if ( temp->next->cnt >= cnt )
{
listNode->next = temp->next;
temp->next = listNode;
list->size ++;
return;
}
temp = temp->next;
}
temp->next = listNode;
}
}
// 创建Huffman树
HuffTree *createHuffTree(LinkList *list)
{
while ( list->first )
{
HuffTree *cnn = (HuffTree *)malloc(sizeof(HuffTree));
HuffNode *node = (HuffNode *)malloc(sizeof(HuffNode));
cnn->Hnode = node;
cnn->Hnode->left = list->first;
cnn->Hnode->right = list->first->next;
cnn->cnt = cnn->Hnode->left->cnt + cnn->Hnode->right->cnt;
cnn->next = NULL;
list->size --;
list->first = list->first->next->next;
insertNode(list ,cnn);
if ( list->size <= 1 )
{
return cnn;
}
}
}
// 插入Huffman树结点,并排序(从小到大)。
void insertNode(LinkList *list, HuffTree *cnn)
{
if ( !list->first )
{
return;
}
if (cnn->cnt <= list->first->cnt)
{
cnn->next = list->first;
list->first = cnn;
return;
}
else
{
HuffTree *temp = list->first;
while ( temp->next )
{
if (temp->next->cnt >= cnn->cnt)
{
cnn->next = temp->next;
temp->next = cnn;
return;
}
temp = temp->next;
}
temp->next = cnn;
}
}
Encode.c
#include "statement.h"
// 给字符串编码
ElemType *encode(HuffTree *root, ElemType *str)
{
Stack stack;
createStack(&stack);
recurse(root, &stack);
destoryStack(&stack);
return splice(code, str);
}
// 创建一个栈,记录走过的结点
void createStack(Stack *stack)
{
stack->base = (ElemType *)malloc(MAX_SIZE * sizeof(ElemType));
if ( !stack->base )
{
printf("Application failed!\n");
exit(0);
}
stack->top = stack->base;
stack->STACK_SIZE = MAX_SIZE;
}
// 递归遍历整棵树(回溯),找到每个字母对应的编码
void recurse(HuffTree *root, Stack *stack)
{
if ( !root->Hnode->left )
{
ElemType c = root->Hnode->data;
ElemType *temp = stack->base;
int i=0;
while ( temp != stack->top )
{
codeTable[(int) c][i] = *temp;
i++;
temp++;
}
stack->top --;
}
else
{
*(stack->top) = '0';
stack->top ++;
recurse(root->Hnode->left, stack);
*(stack->top) = '1';
stack->top ++;
recurse(root->Hnode->right, stack);
stack->top --;
}
}
// 根据字符串拼接编码
ElemType *splice(ElemType *code, ElemType *str)
{
for (int i = 0; i < strlen(str); i++)
{
strcat(code, codeTable[(int) str[i]]);
}
return code;
}
// 销毁栈
void destoryStack(Stack *stack)
{
free(stack->base);
stack->base = stack->top = NULL;
stack->STACK_SIZE = 0;
}
Decode.c
#include "statement.h"
// 解码
ElemType *decode(HuffTree *root, ElemType *code)
{
HuffTree *temp = root;
int i = 0;
int index = 0;
while ( i < strlen(code) )
{
if ( code[i] == '0' )
{
temp = temp->Hnode->left;
}
else
{
temp = temp->Hnode->right;
}
if ( !temp->Hnode->left )
{
resStr[index] = temp->Hnode->data;
temp = root;
index ++;
}
i++;
}
destroyHuffTree(root);
return resStr;
}
// 递归销毁整棵树
void destroyHuffTree(HuffTree *root)
{
if ( root->Hnode->left || root->Hnode->right)
{
destroyHuffTree(root->Hnode->left);
free(root->Hnode->left->Hnode);
free(root->Hnode->left);
root->Hnode->left = NULL;
destroyHuffTree(root->Hnode->right);
free(root->Hnode->right->Hnode);
free(root->Hnode->right);
root->Hnode->right = NULL;
}
}
运行结果 :