使用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语言数据结构实例讲解单链表的实现 单链表是一种线性的数据结构,它由一系列节点组成,每个节点都包含一个数据域和一个指向下一个节点的指针域。单链表常用于需要频繁插入删除元素的场景中。 单链表的数据结构设计 在C语言中,我们可以使用结构体来定义单链表的节点: typedef struct node { int data; // 数据域 struct node* …

    数据结构 2023年5月17日
    00
  • C语言数据结构之栈和队列的实现及应用

    C语言数据结构之栈和队列的实现及应用 什么是栈和队列? 栈是一种具有后进先出(LIFO)特性的线性数据结构,可以理解为一个只能在一端进行插入和删除操作的数据结构。通常称插入元素的一端为栈顶,删除元素的一端为栈底。 队列是一种具有先进先出(FIFO)特性的线性数据结构,可以理解为一个只能在两端进行插入和删除操作的数据结构。一端进行插入操作,称之为队尾,一端进行…

    数据结构 2023年5月17日
    00
  • C语言数据结构之复杂链表的拷贝

    C语言数据结构之复杂链表的拷贝 什么是复杂链表 在了解如何拷贝复杂链表之前,首先需要知道什么是复杂链表。复杂链表是由多个节点组成的链表,每个节点除了包含普通链表节点的值和指向下一个节点的指针外,还包含一个指向链表中的任意一个节点的指针。因此,每个节点有两个指针:一个指向下一个节点,一个指向任意一个节点。 复杂链表示意图如下: +—+ +—+ +—…

    数据结构 2023年5月17日
    00
  • 「学习笔记」AC 自动机

    「学习笔记」AC 自动机 点击查看目录 目录 「学习笔记」AC 自动机 算法 问题 思路 代码 例题 Keywords Search 玄武密码 单词 病毒 最短母串 文本生成器 背单词 密码 禁忌 前置:「学习笔记」字符串基础:Hash,KMP与Trie。 好像对例题的讲解越来越抽象了? 算法 问题 求 \(n\) 个单词在一个长度为 \(m\) 的文章里出…

    算法与数据结构 2023年5月5日
    00
  • NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步

    NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步 NTP时间同步服务器(频率同步)包含帧同步、载波同步、位同步 京准电子科技官微——ahjzsz 同步的概念   同步技术是数字通信系统中非常重要的技术。一般来说数字通信系统要实现多种同步功能才能实现正确的数据通信任务。其技术目标是实现不同地域收发双方的同步通信互联,实现一致的信息数据交换,因此,通…

    算法与数据结构 2023年4月17日
    00
  • Java 数据结构与算法系列精讲之环形链表

    Java 数据结构与算法系列精讲之环形链表 概述 在本文中,我们将探讨环形链表的相关概念,以及如何使用Java语言实现环形链表的各种操作。我们将依次介绍以下几个部分: 环形链表的基本概念 环形链表的创建 环形链表的遍历 环形链表的插入、删除、查找等操作 环形链表的示例程序 环形链表的基本概念 链表是一种基本的数据结构,是由一组节点组成的序列,每个节点包含数据…

    数据结构 2023年5月17日
    00
  • C语言深入浅出解析二叉树

    C语言深入浅出解析二叉树攻略 什么是二叉树 二叉树是一种树形数据结构,其每个节点最多只有两个子节点,分别称为其左子节点和右子节点。一般采用链式存储方式来实现二叉树,也可以使用数组来存储。 二叉树的遍历 二叉树的遍历分为三种方式:前序遍历,中序遍历和后序遍历。 前序遍历 前序遍历的顺序是先遍历根节点,然后遍历左子树,最后遍历右子树。可以使用递归或栈来实现。 v…

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