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

使用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日

相关文章

  • Java数据结构之单链表的实现与面试题汇总

    Java数据结构之单链表的实现与面试题汇总 一、前言 单链表是数据结构中最基础的数据结构之一,也是在面试时经常会考察的一个知识点。本文将详细介绍单链表的实现过程,并对常见的单链表面试题进行总结,帮助大家深入了解单链表的原理和应用。 二、单链表的实现 单链表是由一些列节点构成的,每个节点包括一个数据和一个指向下一个节点的指针。下面我们将实现一个简单的单链表,并…

    数据结构 2023年5月17日
    00
  • C语言数据结构之堆、堆排序的分析及实现

    C语言数据结构之堆、堆排序的分析及实现 什么是堆 堆(Heap)是一种特殊的树形数据结构,它满足两个条件: 堆是一棵完全二叉树; 堆中任意节点的值总是不大于/不小于其子节点的值。 如果父节点的值不大于所有子节点的值,此堆称为小根堆,又称为最小堆。如果父节点的值不小于所有子节点的值,此堆称为大根堆,又称为最大堆。 堆通常可以使用数组来实现,具体实现方法是将堆的…

    数据结构 2023年5月17日
    00
  • C++高级数据结构之优先队列

    C++高级数据结构之优先队列 什么是优先队列? 优先队列是一种特殊的队列,其中每个元素都有一个优先级。当加入一个元素时,它会被放置在队列中的适当位置,以确保优先级最高的元素位于队头。从队列中取出元素时,总是从队头删除元素。 优先队列的应用 优先队列的常见应用场景包括: 操作系统任务调度 网络传输协议TCP中的拥塞控制算法 各种图像算法如边缘检测等 C++中S…

    数据结构 2023年5月17日
    00
  • C++数据结构之list详解

    C++数据结构之list详解 什么是list? list是C++ STL库中的一个数据结构,它能够以O(1)的复杂度在任何位置进行插入或删除操作,当然它也支持随机访问指定位置的元素。list属于双向链表,它内部结构为指针连接不同的节点。 如何使用list? 包含头文件 在C++中使用list,需要包含头文件#include <list>。 定义l…

    数据结构 2023年5月17日
    00
  • Java面试题冲刺第十九天–数据库(4)

    本篇攻略是针对Java数据库相关面试题的,为了方便浏览,我将其分为以下几个部分: 1. 数据库连接池 在Java开发中,我们使用JDBC连接数据库进行数据操作时,为了提高数据库访问性能,通常会使用数据库连接池技术。常见的数据库连接池有:C3P0、Druid、HikariCP等。 C3P0 C3P0是一个开源的数据库连接池,可以设置最大连接数、最小连接数、最大…

    数据结构 2023年5月17日
    00
  • JavaScript 数据结构之散列表的创建(2)

    下面是详细讲解“JavaScript 数据结构之散列表的创建(2)”的完整攻略。 散列表(哈希表) 散列表是一种数据结构,使用哈希函数将键映射到索引。散列表可以在常量时间 O(1) 中进行插入、删除和查找操作,但也具有碰撞冲突问题。 碰撞冲突问题 在散列表中,当两个不同的键通过哈希函数映射到了同一个索引位置时,就会发生碰撞冲突问题。解决碰撞冲突问题的方法有很…

    数据结构 2023年5月17日
    00
  • 【牛客小白月赛69】题解与分析A-F【蛋挞】【玩具】【开题顺序】【旅游】【等腰三角形(easy)】【等腰三角形(hard)】

    比赛传送门:https://ac.nowcoder.com/acm/contest/52441 感觉整体难度有点偏大。 ? 作者:Eriktse? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?? 个人博客:www.eriktse.com A-蛋…

    算法与数据结构 2023年4月18日
    00
  • 在matlab中创建类似字典的数据结构方式

    当需要使用类似字典的数据结构时,Matlab中可以使用结构体来实现。结构体是一种有序的数据集合,每个元素都可以包含不同类型的数据(如字符串、数值等),并通过指定一个名称来唯一地标识该元素。 创建一个空结构体 使用struct函数可以创建一个空的结构体,可以使用下面的代码: st = struct; 添加键值对 可以将键值对添加到结构体中,可以使用下面的代码向…

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