赫夫曼树和赫夫曼编码

在数据膨胀,信息爆炸的今天,数据压缩的意义不言而喻。谈到数据压缩,就不能不提赫夫曼(Huffman)编码,赫夫曼编码是首个实用的压缩编码方案,即使在今天的许多知名压缩算法里,依然可以见到赫夫曼编码的影子。
另外在数据通信中,用二进制给每个字符进行编码时不得不面对的一个问题是如何使电文总长度最短而不产生二义性。根据字符出现的频率,利用赫夫曼树可以构造出一种不等长度的二进制,使编码后的电文长度最短,而保证不产生二义性。

赫夫曼树

以下程序在效率上有什么问题呢?

图片.png
图片.png

根据老师出的一份试卷各个分数段人数的比例, 可以看出%70的70-89分数段的人数要进行两次比较才能判断出成绩。

我们把上面的流程改为以下,效果可能有明显的改善:

图片.png

我们先把这两颗二叉树简化成叶子结点带权的二叉树(注·树结点间的连线相关的数叫做权,Weight)。

图片.png

相关定义

1. 结点的路径长度: 从根结点到该结点的路径上的连接数。
2. 树的路径长度:树中每个叶子结点的路径长度之和。
3. 结点带权路径长度: 结点的路径长度与结点权值得乘积。
4.树的带权路径长度: WPL(Weighted Path Length)是树中所有叶子结点的带权路径长度之和。

WPL的值越小,说明构造出来的二叉树性能越优。
下面演示了用Huffman算法构造一棵Huffman树的过程:


0_1331433273zW25.gif.jpg

赫夫曼编码

赫夫曼编码可以很有效地压缩数据(通常可以节省20%-90%的空间,具体压缩率依赖于数据的特性)。
名词解释: 定长编码,变长编码,前缀码
定长编码: 像ASCII编码
变长编码: 单个编码的长度不一致,可以根据整体出现频率来调节
前缀码:所谓的前缀码,就是没有任何码字是其他码字的前缀

赫夫曼编码的整体结构示意图如下:


图片.png

赫夫曼编码的 编码和解码的代码实现:
queue.h

#pragma once
#ifndef _PQUEUE_H
#define _PQUEUE_H

#include "huffman.h"
#define TYPE htNode *
#define MAX_SZ 256
typedef struct _pQueueNode {
    TYPE val;
    unsigned int priority;
    struct _pQueueNode *next;
}pQueueNode;

typedef struct _pQueue {
    unsigned int size;
    pQueueNode *first;
}pQueue;

void initPQueue(pQueue **queue);
void addPQueue(pQueue **queue, TYPE val, unsigned int priority);
TYPE getPQueue(pQueue **queue);

#endif // !_PQEUE_H

queue.cpp

#include "stdafx.h"
#include "queue.h"
#include <stdlib.h>
#include <stdio.h>

void initPQueue(pQueue **queue) {
    (*queue) = (pQueue *)malloc(sizeof(pQueue));
    (*queue)->first = NULL;
    (*queue)->size = 0;
    return;
}

void addPQueue(pQueue **queue, TYPE val, unsigned int priority) {
    if ((*queue)->size == MAX_SZ)
    {
        printf("\nQueue is full.\n");
        return;
    }
    pQueueNode *aux = (pQueueNode *)malloc(sizeof(pQueueNode));
    aux->priority = priority;
    aux->val = val;
    if ((*queue)->size == 0 || (*queue)->first == NULL)
    {
        aux->next = NULL;
        (*queue)->first = aux;
        (*queue)->size = 1;
        return;
    }
    else
    {
        if (priority <= (*queue)->first->priority)
        {
            aux->next = (*queue)->first;
            (*queue)->first = aux;
            (*queue)->size++;
            return;
        }
        else
        {
            pQueueNode *iterator = (*queue)->first;
            while (iterator->next != NULL)
            {
                if (priority <= iterator->next->priority)
                {
                    aux->next = iterator->next;
                    iterator->next = aux;
                    (*queue)->size++;
                    return;
                }
                iterator = iterator->next;
            }
            if (iterator->next == NULL)
            {
                aux->next = NULL;
                iterator->next = aux;
                (*queue)->size++;
                return;
            }
        }
    }
}

TYPE getPQueue(pQueue **queue) {
    TYPE returnValue = NULL;
    if ((*queue)->size > 0)
    {
        returnValue = (*queue)->first->val;
        (*queue)->first = (*queue)->first->next;
        (*queue)->size--;
    }
    else
    {
        printf("\nQueue is empty.\n");
    }
    return returnValue;
}

huffman.h

#pragma once
#ifndef _HUFFMAN_H
#define _HUFFMAN_H

typedef struct _htNode {
    char symbol;
    struct _htNode *left, *right;
}htNode;

typedef struct _htTree {
    htNode *root;
}htTree;

typedef struct _hlNode {
    char symbol;
    char *code;
    struct _hlNode *next;
}hlNode;

typedef struct _hlTable
{
    hlNode *first;
    hlNode *last;
}hlTable;

#endif // !_HUFFMAN_H

huffman.cpp

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "huffman.h"
#include "queue.h"
#pragma warning(disable:4996)
void traverseTree(htNode *treeNode, hlTable **table, int k, char code[256]) {
    if (treeNode->left == NULL && treeNode->right == NULL)
    {
        code[k] = '\0';
        hlNode *aux = (hlNode *)malloc(sizeof(hlNode));
        aux->code = (char *)malloc(sizeof(char) * (strlen(code) + 1));
        strcpy(aux->code, code);
        //strcpy_s(aux->code, strlen(code), code);
        aux->symbol = treeNode->symbol;
        aux->next = NULL;
        if ((*table)->first == NULL)
        {
            (*table)->first = aux;
            (*table)->last = aux;
        }
        else
        {
            (*table)->last->next = aux;
            (*table)->last = aux;
        }
    }
    if (treeNode->left != NULL)
    {
        code[k] = '0';
        traverseTree(treeNode->left, table, k + 1, code);
    }
    if (treeNode->right != NULL)
    {
        code[k] = '1';
        traverseTree(treeNode->right, table, k + 1, code);
    }
}

htTree * buildTree(char *inputString) {
    int *probability = (int *)malloc(sizeof(int) * 256);
    //初始化
    for (int i = 0; i < 256; i++)
    {
        probability[i] = 0;
    }
    //统计待编码的字符串各个字符出现的次数
    for (int j = 0; inputString[j] != '\0'; j++)
    {
        probability[(unsigned char)inputString[j]]++;
    }
    //pQueue队列的头指针
    pQueue *huffmanQueue;
    initPQueue(&huffmanQueue);
    //填充队列
    for (int k = 0; k < 256; k++)
    {
        if (probability[k] != 0)
        {
            htNode *aux = (htNode *)malloc(sizeof(htNode));
            aux->left = NULL;
            aux->right = NULL;
            aux->symbol = (char)k;
            addPQueue(&huffmanQueue, aux, probability[k]);
        }
    }
    free(probability);
    //生成huffman树
    while (huffmanQueue->size != 1)
    {
        int priority = huffmanQueue->first->priority;
        priority += huffmanQueue->first->next->priority;
        htNode *left = getPQueue(&huffmanQueue);
        htNode *right = getPQueue(&huffmanQueue);

        htNode *newNode = (htNode *)malloc(sizeof(htNode));
        newNode->left = left;
        newNode->right = right;
        addPQueue(&huffmanQueue, newNode, priority);
    } 
    htTree *tree = (htTree *)malloc(sizeof(htTree));
    tree->root = getPQueue(&huffmanQueue);
    return tree;
}

hlTable *buildTable(htTree *huffmanTree) {
    hlTable *table = (hlTable *)malloc(sizeof(hlTable));
    table->first = NULL;
    table->last = NULL;
    char code[256];
    int k = 0;
    traverseTree(huffmanTree->root, &table, k, code);
    return table;
}

void encode(hlTable *table, char *stringToEncode) {
    hlNode *traversal;
    printf("Encoding...\n\nInput string : \n %s\n\nEncoding string : \n", stringToEncode);
    for (int i = 0; stringToEncode[i] != '\0'; i++)
    {
        traversal = table->first;
        while (traversal->symbol != stringToEncode[i])
        {
            traversal = traversal->next;
        }
        printf("%s", traversal->code);
    }
    printf("\n");
}

void decode(htTree *tree, char *stringToDecode) {
    htNode *traversal = tree->root;
    printf("\n\nDecoding...\n\nInput string : \n%s\n\nDecoded string : \n", stringToDecode);
    for (int i = 0; stringToDecode[i] != '\0'; i++)
    {
        if (traversal->left == NULL && traversal->right == NULL)
        {
            printf("%c", traversal->symbol);
            traversal = tree->root;
        }
        if (stringToDecode[i] == '0')
        {
            traversal = traversal->left;
        }
        if (stringToDecode[i] == '1')
        {
            traversal = traversal->right;
        }
        if (stringToDecode[i] != '0' && stringToDecode[i] != '1')
        {
            printf("The input string is not coded correctly!\n");
            return;
        }
    }
    if (traversal->left == NULL && traversal->right == NULL)
    {
    }
}


int main() {
    htTree *codeTree = buildTree("I love you!");
    hlTable *codeTable = buildTable(codeTree);
    encode(codeTable, "I love you");
    decode(codeTree, "1011111");
    
    getchar();
    getchar();
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容

  • 日常生活中经常会使用到文件压缩,但是基本很少有人会问到压缩是如何实现的。所谓的,就是把文本重新进行编码,减少不必要...
    ZhengYaWei阅读 2,948评论 2 15
  • 对于我们的日常操作压缩文件来说,通常都是将文件中的字符转换成压缩后的格式,但为什么能够解压回来,那是因为压缩后的数...
    Forget_ever阅读 4,156评论 0 8
  • 最近在看图像的压缩,就想着先实现一个jpeg文件的解码。本来以为这种资料在网上会一搜一大堆,但搜了之后才发现很多网...
    月夢書阅读 10,053评论 4 13
  • 9.3.3 快速排序   快速排序将原数组划分为两个子数组,第一个子数组中元素小于等于某个边界值,第二个子数组中的...
    RichardJieChen阅读 1,787评论 0 3
  • 年少的时候,我有过几次“鸟屎砸头”的经历,我以为压马路所遇到最倒霉的事莫过于此。万万没想到,我也有成为“失足少女”...
    Wendy0824阅读 1,749评论 0 0