使用C语言详解霍夫曼树数据结构

yizhihongxing

使用C语言详解霍夫曼树数据结构

什么是霍夫曼树

霍夫曼树是一种带权路径长度最短的树,也称为最优二叉树,它是优化编码的核心算法。

在霍夫曼树中,每个叶子节点对应一个字符,该节点的权值为该字符出现的次数。当然,字符可以是任何数据类型。生成霍夫曼树后,在对每个字符进行编码时,字符在霍夫曼树中的路径即为其编码。(一般规定,一条从根到叶子的路径上只出现0或1,从根到某个叶子节点的路径就是该叶子节点对应字符的哈夫曼编码。)

如何生成霍夫曼树

生成霍夫曼树的过程是:首先,把所有的节点按权值从小到大排序,选出权值最小的两个节点组成一棵新的二叉树,它们成为新二叉树的左右子树,新二叉树的根节点的权值是它的左右子树权值之和。在原来的节点序列中,把这两个最小权值的节点删除,并把新生成的根节点加入节点序列。重复上述过程,直到序列中只剩下一个节点为止,这个节点即为霍夫曼树的根节点。

下面是示例代码:

typedef struct _HuffmanTreeNode
{
    int weight;                 // 权重(节点存储的值)
    struct _HuffmanTreeNode *lchild, *rchild;   // 左右子树
}HuffmanTreeNode;

HuffmanTreeNode* CreateHuffmanTree(int weight[], int n)
{
    // 初始化n个叶子结点
    HuffmanTreeNode **nodeList = (HuffmanTreeNode**)malloc(sizeof(HuffmanTreeNode*) * n);
    for (int i = 0; i < n; i++) 
    {
        nodeList[i] = (HuffmanTreeNode*)malloc(sizeof(HuffmanTreeNode));
        nodeList[i]->weight = weight[i];
        nodeList[i]->lchild = nodeList[i]->rchild = NULL;
    }

    // 生成哈夫曼树
    while(n > 1)
    {
        int min1 = 0, min2 = 1;
        if(nodeList[min1]->weight > nodeList[min2]->weight)
        {
            int tmp = min1;
            min1 = min2;
            min2 = tmp;
        }
        for(int i = 2; i < n; i++)
        {
            if(nodeList[i]->weight < nodeList[min1]->weight)
            {
                min2 = min1;
                min1 = i;
            }
            else if(nodeList[i]->weight < nodeList[min2]->weight)
            {
                min2 = i;
            }
        }

        // 组成新的二叉树
        HuffmanTreeNode *newNode = (HuffmanTreeNode*)malloc(sizeof(HuffmanTreeNode));
        newNode->weight = nodeList[min1]->weight + nodeList[min2]->weight;
        newNode->lchild = nodeList[min1];
        newNode->rchild = nodeList[min2];
        nodeList[min1] = newNode;//将两个子节点合并到一个新节点
        nodeList[min2] = nodeList[n-1];//注意将n-1赋值,因为有一个原叶子节点已经被合并
        n--;
    }
    return nodeList[0];
}

如何通过霍夫曼树来编码

霍夫曼编码存在优美的数学性质:任意一个字符的编码都不是另一个字符编码的前缀,所以解码时不会发生歧义。

下面是示例代码:

void GetHuffmanCode(HuffmanTreeNode* root, char* code, char** codes, int depth)
{
    static int count = 0;               // 存储编码字符串的长度
    if(root == NULL)                    // 树到了根节点
        return ;
    if(root->lchild == NULL && root->rchild == NULL)
    {
        code[count] = '\0';
        codes[root->weight] = (char*)malloc(sizeof(char)*count);
        strcpy(codes[root->weight], code);
        return ;
    }
    count++;
    code[count-1] = '0';//左子树编码为0
    GetHuffmanCode(root->lchild, code, codes, depth + 1);
    code[count-1] = '1';//右子树编码为1
    GetHuffmanCode(root->rchild, code, codes, depth + 1);
    count--;
}

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct _HuffmanTreeNode
{
    int weight;                 // 权重(节点存储的值)
    struct _HuffmanTreeNode *lchild, *rchild;   // 左右子树
}HuffmanTreeNode;

HuffmanTreeNode* CreateHuffmanTree(int weight[], int n)
{
    // 初始化n个叶子结点
    HuffmanTreeNode **nodeList = (HuffmanTreeNode**)malloc(sizeof(HuffmanTreeNode*) * n);
    for (int i = 0; i < n; i++) 
    {
        nodeList[i] = (HuffmanTreeNode*)malloc(sizeof(HuffmanTreeNode));
        nodeList[i]->weight = weight[i];
        nodeList[i]->lchild = nodeList[i]->rchild = NULL;
    }

    // 生成哈夫曼树
    while(n > 1)
    {
        int min1 = 0, min2 = 1;
        if(nodeList[min1]->weight > nodeList[min2]->weight)
        {
            int tmp = min1;
            min1 = min2;
            min2 = tmp;
        }
        for(int i = 2; i < n; i++)
        {
            if(nodeList[i]->weight < nodeList[min1]->weight)
            {
                min2 = min1;
                min1 = i;
            }
            else if(nodeList[i]->weight < nodeList[min2]->weight)
            {
                min2 = i;
            }
        }
        // 组成新的二叉树
        HuffmanTreeNode *newNode = (HuffmanTreeNode*)malloc(sizeof(HuffmanTreeNode));
        newNode->weight = nodeList[min1]->weight + nodeList[min2]->weight;
        newNode->lchild = nodeList[min1];
        newNode->rchild = nodeList[min2];
        nodeList[min1] = newNode;//将两个子节点合并到一个新节点
        nodeList[min2] = nodeList[n-1];//注意将n-1赋值,因为有一个原叶子节点已经被合并
        n--;
    }
    return nodeList[0];
}

void GetHuffmanCode(HuffmanTreeNode* root, char* code, char** codes, int depth)
{
    static int count = 0;               // 存储编码字符串的长度
    if(root == NULL)                    // 树到了根节点
        return ;
    if(root->lchild == NULL && root->rchild == NULL)
    {
        code[count] = '\0';
        codes[root->weight] = (char*)malloc(sizeof(char)*count);
        strcpy(codes[root->weight], code);
        return ;
    }
    count++;
    code[count-1] = '0';//左子树编码为0
    GetHuffmanCode(root->lchild, code, codes, depth + 1);
    code[count-1] = '1';//右子树编码为1
    GetHuffmanCode(root->rchild, code, codes, depth + 1);
    count--;
}

void PrintCode(char** codes,int len)
{
    for(int i=0;i<len;i++)
    {
        printf("%d %s\n",i,codes[i]);
    }
}

int main()
{
    int weight[] = {5, 6, 8, 7, 15 ,10};
    char* codes[6] = {0};
    char code[100];
    HuffmanTreeNode *root = CreateHuffmanTree(weight, 6);
    GetHuffmanCode(root, code, codes, 0);
    PrintCode(codes,6);
    return 0;
}

以上代码的输出结果为:

0 011
1 010
2 111
3 110
4 00
5 101

其中第i行表示值为i的节点对应的编码为code[i]。

总结

霍夫曼树不仅可以用于编码和压缩,还可以应用于诸如数据存储、加密和网络通信等领域,掌握霍夫曼树对于算法理解和应用编程都有很大的帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用C语言详解霍夫曼树数据结构 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • js实现无限层级树形数据结构(创新算法)

    要实现无限层级树形数据结构,可以使用递归算法来解决。以下是该过程的详细攻略: 步骤1:准备数据 为了演示无限层级树形结构,我们需要准备一组具有父子关系的数据。数据可以是任何格式,例如在子对象节点下添加一个名为children的数组即可。 例如,假设我们有以下数据: const data = [ { id: 1, name: "Node 1&quot…

    数据结构 2023年5月17日
    00
  • Java数据结构之双向链表图解

    以下是Java数据结构之双向链表图解的完整攻略: 一、双向链表简介 1.1 定义 双向链表(Doubly Linked List),也叫双向链式线性表,是链式存储结构的基本形式之一。双向链表的每个节点都含有两个指针,分别指向前驱节点和后继节点,因此可以支持双向遍历。 1.2 结构 双向链表的结构可以用下图表示: +——-+ +——-+ +–…

    数据结构 2023年5月17日
    00
  • 一起来看看C语言线性表的线性链表

    一起来看看C语言线性表的线性链表攻略 线性链表概述 线性链表是线性表的一种实现方式,它通过每个节点中包含指向下一个节点的指针来实现表中元素之间的链接,可以动态地增加、删除节点。线性链表分为带头节点的链表和不带头节点的链表,其中带头节点的链表更为常见。 实现思路 结构体定义 我们可以定义一个结构体来表示每个节点,例如: typedef struct ListN…

    数据结构 2023年5月17日
    00
  • C++超详细讲解单链表的实现

    首先我们来了解一下单链表的概念。 单链表是一种常见的数据结构,在计算机科学中被广泛使用。它是由节点所组成的数据结构,其中每个节点都包含两部分,一个是存储数据的元素,另一个是指向下一个节点的指针。单链表的首节点被称为头部,而最后一个节点则被称为尾部。单链表可以通过在头部插入和删除元素来实现高效地数据操作。接下来我们将讲解如何实现一个 C++ 版的单链表。 实现…

    数据结构 2023年5月17日
    00
  • JS中的算法与数据结构之链表(Linked-list)实例详解

    JS中的算法与数据结构之链表(Linked-list)实例详解 什么是链表? 链表是计算机科学中的一种数据结构,由一系列结点(Link,也称为节点)组成,并通过每个节点中的指针(Pointer)链接在一起。每个节点包含数据和一个指向某个位置的引用。 链表的主要特点是在插入和删除操作中表现出很高的效率。与数组相比,链表的访问和操作速度较慢,但在处理动态结构数据…

    数据结构 2023年5月17日
    00
  • python数据结构之二叉树的建立实例

    下面是Python数据结构之二叉树的建立实例的完整攻略。 一、二叉树的概念 二叉树是一种常用的树形结构,它由一组父子关系组成,其中每个节点都最多有两个子节点。通常我们认为,一个二叉树的根节点是唯一的,它没有父节点,而叶子节点则没有子节点。在二叉树中,节点的左子树和右子树可以为空。 二、二叉树的遍历 二叉树的遍历是指访问二叉树中所有节点的操作,它分为三种不同的…

    数据结构 2023年5月17日
    00
  • Java数据结构之线索化二叉树的实现

    Java数据结构之线索化二叉树的实现 线索化二叉树的概述 线索化二叉树(Threaded Binary Tree)是一种优化的二叉树结构。它的优点是可以在O(n)的时间复杂度内,进行中序遍历。而在普通二叉树中进行中序遍历需要的时间复杂度是O(nlogn)。线索化二叉树的原理是利用空闲的指针域,来记录中序遍历中前驱与后继结点的位置。线索化二叉树中会出现两种类型…

    数据结构 2023年5月17日
    00
  • [Week 19]每日一题(C++,数学,并查集,动态规划)

    目录 [Daimayuan] T1 倒数第n个字符串(C++,进制) 输入格式 输出格式 样例输入 样例输出 解题思路 [Daimayuan] T2 排队(C++,并查集) 输入格式 输出格式 样例输入1 样例输出1 样例输入2 样例输出2 样例输入3 样例输出3 数据规模 解题思路 [Daimayuan] T3 素数之欢(C++,BFS) 数据规模 输入格…

    算法与数据结构 2023年5月4日
    00
合作推广
合作推广
分享本页
返回顶部