Huffman树及其编解码

Huffman树——编解码

介绍:

??Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于数据压缩或者解压缩,和对字符的编解码。

??可是Huffman树的优点在哪?

??1、就在于它对出现次数大的字符(即权值大的字符)的编码比出现少的字符编码短,也就是说出现次数越多,编码越短,保证了对数据的压缩。
??2、保证编的码不会出现互相涵括,也就是不会出现二义性,比如a的编码是00100,b的编码是001,而c的编码是00,,这样的话,对于00100就可能是a,也可能是bc,而Huffman树编码方式不会出现这种问题。

如何实现

??实现Huffman树的编解码需要三种数据类型,一个是优先级队列,用来保存树的结点,二是树,用来解码,三是表,用来当作码表编码。下面我们先一一介绍一下三种数据结构:

1、优先级队列

??优先级队列里存放的是一个一个的树的结点,根据树结点中存放的字符的权值来确定其优先级,权重越小,优先级越小,放的位置越靠前。也就是说第一个结点存放的优先级最小,权值最小。

数据类型
//优先级队列,struct TNode表示树的结点,在后面介绍
typedef struct QNode
{
    struct TNode* val;          //树的结点,其实也就是数据域
    int priority;               //优先级
    struct QNode* next;         //指针域
}*Node;

typedef struct Queue
{
    int size;           //队列大小
    struct QNode* front;        //队列头指针
}queue;

2、树

??树里面存放的是字符,以及指向自己的左右孩子结点的指针。比如下图,虽然下图中看起来书中存放了该字符的优先级,但其实可以不加,感觉比较繁琐,所以我取了,但是为了理解方便起见,我在图上标注了出来。

数据类型
//树
typedef struct TNode
{
    char data;              //字符值
    struct TNode* left;         //左孩子
    struct TNode* right;                //右孩子
}*Tree;

3、表

??这个表其实就是一张编码表,里面存放了字符和该字符的编码,用于编码的时候查看。

数据类型
//表
typedef struct BNode
{
    char code[256];             //编码
    char symbol;                //字符
    struct BNode* next;         //指向下一个
}*bNode;

typedef struct Table
{
    struct BNode* first;                //表头
    struct BNode* last;             //表尾
}*table;

思路

??为了简单起见我们讲述的时候就先将权值设置为用户输入而不是根据出现频率统计,因为我们作业也刚好是用户输入,文章最后我会贴出根据出现频率统计的代码,有兴趣可以看看。因为用到了很多数据类型所以可能写到一半会觉得有点晕,所以我们开始之前先理一下思路:

先设定a,b,c三个数据,它们的权值分别为6,1,2

??1、首先要根据用户输入的每个字符的权值,创建出一个一个的树结点,然后将其按照优先级的大小存入优先级队列中,按从小到大的顺序,具体实现我会在后面贴。

??2、根据优先级队列中存放的树的结点构建起一棵树。

??先出队前两个结点,然后创建一个新的树的结点,新的树的结点的权值就等于出队的两个结点的权值之和,但其没有字符域,也就是说它不是一个真正的树的结点,我们称其为假树结点,对应称为真树结点。
??让出队的两个真树结点作为新得到的假树结点的左右孩子,优先级小的真树结点(也就是先出队的真树结点)作为左孩子,另一个为右孩子。


出队后

b和c为真树结点,最上面权值为3的为假树结点

??最后将新创建的假树结点又入队,继续循环操作,直到队列只剩一个结点,那个结点就是假树结点,最后也要作为Huffman树的根节点root。

新的假树结点入队后

到最后就是下面这样
队列只剩最后一个假树结点,而且作为所构建Huffman树的根节点root

??3、遍历整棵树建起一张码表,通过观察我们发现,真正有意义的真树结点其实都是叶子节点,所以我们在遍历的时候将所有的叶子节点的编码和字符存入表中即可。
??我们规定遍历树建立表的时候,往左孩子访问一层给码值加0,往右就加1。比如刚刚介绍树的时候贴的那张图,b是00,c是01,a是1。

下面是建立起来的码表

构建Huffman树和创建编码表的实现过程

??看完思路之后再看实现过程,我们先看创建队列时候的一系列操作:

??因为为了方便我用了部分C++语法,所以分配内存会是用new,释放内存就是delete,就和C语言里malloc和free是一个作用,其他的都一样。

?队列的初始化:

queue Init_queue()
{
    queue q;
    q.size = 0;
    q.front = new struct QNode;
    if (!q.front)
    {
        printf("分配失败!\n");
        exit(1);
    }
    q.front->next = NULL;
    return q;
}

?队列的插入:

//插入,根据优先级
bool EnQueue(queue& q, Tree avl, int weight)
{
    Node newp = new struct QNode;
    newp->val = avl;
    newp->priority = weight;
    if (q.size == 0 || q.front == NULL)         //空表
    {
        newp->next = NULL;
        q.front = newp;
        q.size = 1;
        return true;
    }
    else        //中间位置,需要迭代
    {
        if (weight <= q.front->priority)    //比第一个都小
        {
            newp->next = q.front;
            q.front = newp;
            q.size++;
            return true;
        }
        else    //中间位置
        {
            Node beforp = q.front;
            while (beforp->next != NULL)
            {
                if (weight <= beforp->next->priority)
                {
                    newp->next = beforp->next;
                    beforp->next = newp;
                    q.size++;
                    return true;
                }
                else
                {
                    beforp = beforp->next;
                }
            }
            //需要插在队列最后
            if (beforp->next == NULL)
            {
                newp->next = NULL;
                beforp->next = newp;
                q.size++;
                return true;
            }
        }
    }
    return true;
}

创建一个队列:
需要用户输入每个字符和对应的优先级

//创建队列
queue Create_Queue()
{
    queue q = Init_queue();
    while (1)
    {
        char symbol;
        int weight;
        cin >> symbol >> weight;    //C++里的输入,输入symnol和weight
        if (weight == 0)  //如果输入的权值为0,表示输入结束
            break;
        Tree t = new struct TNode;
        t->data = symbol;
        t->left = NULL;
        t->right = NULL;
        EnQueue(q, t, weight);
    }
    return q;
}

弹出队列中优先级最小的结点:

//弹出队列优先级最小的
Tree Dequeue(queue& q)
{
    if (q.front == NULL)
    {
        cout << "空队!" << endl;
        exit(1);
    }
    Node p = q.front;
    q.front = p->next;
    Tree e = p->val;
    q.size--;
    delete[] p;
    return e;
}

树的函数,根据优先级队列创建一棵树:

//树的函数
//创建一棵树
Tree Create_Tree(queue& q)
{
    while (q.size != 1)
    {
        int priority = q.front->priority + q.front->next->priority;
        Tree left = Dequeue(q);
        Tree right = Dequeue(q);

        Tree newTNode = new struct TNode;
        newTNode->left = left;
        newTNode->right = right;

        EnQueue(q, newTNode, priority);
    }
    Tree root = new struct TNode;
    root = Dequeue(q);
    return root;
}

表的函数,根据树创建一张表:

//创建一张表
table Create_Table(Tree root)
{
    table t = new struct Table;
    t->first = NULL;
    t->last = NULL;
    char code[256];
    int k = 0;
    travel(root, t, code, k);
    return t;
}

表的函数,对travel函数的实现:
travel函数表示对树的遍历,从而建立起表,采用表尾插入法

void travel(Tree root, table& t, char code[256], int k)
{
    if (root->left == NULL && root->right == NULL)
    {
        code[k] = '\0';

        bNode b = new struct BNode;
        b->symbol = root->data;
        strcpy(b->code, code);
        b->next = NULL;

        //尾部插入法
        if (t->first == NULL)       //空表
        {
            t->first = b;
            t->last = b;
        }
        else
        {
            t->last->next = b;
            t->last = b;
        }
    }
    if (root->left != NULL)
    {
        code[k] = '0';
        travel(root->left, t, code, k + 1);
    }
    if (root->right != NULL)
    {
        code[k] = '1';
        travel(root->right, t, code, k + 1);
    }
}

编解码

??至此,Huffman树以及编码表已经构建完毕,现在就来实现编解码的函数来检验上述的Huffman树。

编码:
需要传入编码表来进行编码

void EnCode(table t, char* str)
{
    cout << "EnCodeing............./" << endl;
    int len = strlen(str);
    for (int i = 0; i < len; i++)
    {
        bNode p = t->first;
        while (p != NULL)
        {
            if (p->symbol == str[i])
            {
                cout << p->code;
                break;
            }
            p = p->next;
        }
    }
    cout << endl;
}

解码:
需要传入Huffman树来进行编码

void DeCode(Tree root, char* str)
{
    cout << "DeCode............./" << endl;
    Tree p = root;
    int len = strlen(str);
    for (int i = 0; i < len; i++)
    {
        if (p->left == NULL && p->right == NULL)
        {
            cout << p->data;
            p = root;
        }
        if (str[i] == '0')
            p = p->left;
        if (str[i] == '1')
            p = p->right;
        if (str[i] != '0' && str[i] != '1')
        {
            cout << "The Input String Is Not Encoded correctly !" << endl;
            return;
        }
    }
    if (p->left == NULL && p->right == NULL)
        cout << p->data;
        cout << endl;
}

测试数据

int main()
{
    queue q = Create_Queue();
    Tree root = Create_Tree(q);
    table t = Create_Table(root);
    char str[256];
    cout << "请输入要编码的字符:" << endl;
    cin >> str;
    EnCode(t, str);
    cout << "请输入要解码的码值:" << endl;
    char str1[256];
    cin >> str1;
    DeCode(root, str1);
}

附上截图:

原文地址:https://www.cnblogs.com/vfdxvffd/p/11622261.html

时间: 2024-11-05 06:28:14

Huffman树及其编解码的相关文章

哈夫曼树 之 建树和编解码

/* * 实现过程:着先通过 HuffmanTree() 函数构造哈夫曼树,然后在主函数 main()中 * 自底向上开始(也就是从数组序号为零的结点开始)向上层层判断,若在 * 父结点左侧,则置码为 0,若在右侧,则置码为 1.最后输出生成的编码. *------------------------------------------------------------------------*/ #include <stdio.h> #include<stdlib.h> #in

JPEG文件编/解码详解

JPEG文件编/解码详解(1) JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写.它由国际电话与电报咨询委员会CCITT(The International Telegraph and Telephone Consultative Committee)与国际标准化组织ISO于1986年联合成立的一个小组,负责制定静态数字图像的编码标准. 小组一直致力于标准化工作,开发研制出连续色调.多级灰度.静止图像的数字图像压缩编码方法,即JPEG算法.

哈夫曼(Huffman)树与哈夫曼编码

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289610.html 哈夫曼树又称最优二叉树,是一种带权路径长最短的树.树的路径长度是从树根到每一个叶子之间的路径长度之和.节点的带树路径长度为从该节点到树根之间的路径长度与该节点权(比如字符在某串中的使用频率)的乘积. 比如有一串字符串如

视频编解码学习之三:变换,量化与熵编码

第6章 变换编码 1. 变换编码 变换编码的目的 去除空间信号的相关性 将空间信号的能力集中到频域的一小部分低频系数上 能量小的系数可通过量化去除,而不会严重影响重构图像的质量 块变换和全局变换 块变换:离散余弦变换(Discrete Cosine Transform,DCT),4x4,8x8,16x16 全局变换:小波变换(Wavelet) 变换的能量集中特性 DCT编码 2. 变换类型 K-L变换 傅里叶变换 余弦变换 小波变换 3. KL变换 最优变换 基函数根据具体图像而确定 没有快速算

Huffman树及其应用

哈夫曼树又称为最优二叉树,哈夫曼树的一个最主要的应用就是哈夫曼编码,本文通过简单的问题举例阐释哈夫曼编码的由来,并用哈夫曼树的方法构造哈夫曼编码,最终解决问题来更好的认识哈夫曼树的应用--哈夫曼编码. 一.引子 在学习中我们经常遇到将各科成绩改为优秀.良好.中等.及格和不及格.那么根据分级原理,代码表示为: if(a<60) b = "不及格"; else if(a<70) b = "及格"; else if(a<80) b = "中等&

Huffman树与编码

带权路径最小的二叉树称为最优二叉树或Huffman(哈夫曼树). Huffman树的构造 将节点的权值存入数组中,由数组开始构造Huffman树.初始化指针数组,指针指向含有权值的孤立节点. b = malloc(n*sizeof(BTreeNode)); for (i = 0; i < n; i++) { b[i] = malloc(sizeof(BTreeNode)); b[i]->data = a[i]; b[i]->left = NULL; b[i]->right = NU

数据结构之huffman树

#include <stdio.h> #include<stdlib.h> #define MAXBIT      99 #define MAXVALUE  9999 #define MAXLEAF     30 #define MAXNODE    MAXLEAF*2 -1 typedef struct  {     int bit[MAXBIT];     int start; } HCodeType;        /* 编码结构体 */ typedef struct {  

各种音视频编解码学习详解

各种音视频编解码学习详解 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等.最近因为项目的关系,需要理清媒体的codec,比较搞的是,在豆丁网上看运营商的规范 标准,同一运营商同样的业务在不同文档中不同的要求,而且有些要求就我看来应当是历史的延续,也就是现在已经很少采用了.所以豆丁上看不出所以然,从 wiki上查.中文的wiki信息量有限,很短,而wiki的英文内容内多,删减版

视频编解码学习之一:理论基础

转:http://www.cnblogs.com/xkfz007/archive/2012/08/12/2613690.html 第1章介绍 1. 为什么要进行视频压缩? 未经压缩的数字视频的数据量巨大 存储困难 一张DVD只能存储几秒钟的未压缩数字视频. 传输困难 1兆的带宽传输一秒的数字电视视频需要大约4分钟. 2. 为什么可以压缩 去除冗余信息 空间冗余:图像相邻像素之间有较强的相关性 时间冗余:视频序列的相邻图像之间内容相似 编码冗余:不同像素值出现的概率不同 视觉冗余:人的视觉系统对某