Huffman编码、Huffman树(源码)

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;
    }
}

  运行结果 :

​ 

  • 25
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值