哈夫曼树译码函数(Decoding) 该函数通过哈夫曼编码串和已构建的哈夫曼树,还原出原始字符序列

一、哈夫曼树译码函数(Decoding)
该函数通过哈夫曼编码串和已构建的哈夫曼树,还原出原始字符序列。其核心逻辑如下:

  1. 初始状态:从哈夫曼树的根节点开始(在数组表示中,根节点下标通常为 2*n - 1,其中 n 是叶子节点数量)。
  2. 遍历编码串
    • 对于编码串中的每一位:
      • 若为 '0',则跳转到当前节点的左孩子;
      • 若为 '1',则跳转到当前节点的右孩子。
  3. 判断是否到达叶子节点
    • 当前节点的左右孩子均为 0(或为空),说明是叶子节点;
    • 此时输出该节点所代表的字符;
    • 然后重新回到根节点,继续后续译码。
  4. 终止条件:编码串全部位处理完毕,译码结束。

示例代码(C语言风格结构体与数组实现):

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

#define MAX_NODE 100

typedef struct {
    char ch;        // 存储字符
    int weight;     // 权重
    int parent, lchild, rchild; // 双亲、左孩子、右孩子下标
} HTNode;

void HuffmanDecode(HTNode ht[], int root, char *code, int n) {
    int i = 0;
    int current = root;
    int len = strlen(code);

    while (i < len) {
        if (code[i] == '0') {
            current = ht[current].lchild;  // 走左子树
        } else if (code[i] == '1') {
            current = ht[current].rchild;  // 走右子树
        }

        // 判断是否为叶子节点(左右孩子都为0)
        if (ht[current].lchild == 0 && ht[current].rchild == 0) {
            printf("%c", ht[current].ch);  // 输出对应字符
            current = root;  // 回到根节点
        }
        i++;
    }
    printf("\n");
}

二、树的存储结构

  1. 双亲表示法

    • 使用结构数组存储每个节点,每个节点包含数据、权重、双亲下标、左右孩子下标等信息。
    • 优点:便于向上查找祖先节点,适合构造哈夫曼树;
    • 缺点:查找孩子节点效率低,需遍历整个数组。
  2. 孩子表示法

    • 每个节点保存一个链表或动态数组,记录所有孩子节点的下标或指针;
    • 适用于多叉树,如文件系统目录结构;
    • 查找孩子高效,但空间开销较大。
  3. 孩子兄弟表示法(左孩子-右兄弟表示法)

    • 每个节点包含两个指针:“第一个孩子” 和 “右兄弟”;
    • 可将任意树转化为二叉树形式进行存储和操作;
    • 特别适合递归遍历和森林转换为二叉树的应用场景。

这些存储方式广泛应用于数据压缩(如哈夫曼编码)、操作系统目录管理、编译器语法树构建等领域。
构建哈夫曼树并生成编码表是数据压缩中的核心步骤,主要包括两个阶段:建树编码生成。以下是详细过程与实现方法。


一、构建哈夫曼树(Huffman Tree)

基本思想:

采用贪心算法,每次从所有节点中选取权值最小的两个节点,合并成一个新的内部节点,其权值为两者之和,直到只剩一棵树。

步骤:
  1. 给定 n 个字符及其出现频率(或权重),每个字符作为叶子节点。
  2. 构造一个优先队列(最小堆),按权值排序。
  3. 重复以下操作 (n-1) 次:
    • 取出权值最小的两个节点 A 和 B;
    • 创建新节点 C,C 的权值 = A.权值 + B.权值;
    • 将 A 设为 C 的左孩子,B 为右孩子(或反之);
    • 将 C 插入优先队列。
  4. 最后剩下的节点即为哈夫曼树的根。
存储结构(双亲表示法数组):

使用结构体数组 HTNode ht[2*n],索引从 1 开始:

typedef struct {
    char ch;        // 字符
    int weight;     // 权重
    int parent;     // 双亲下标
    int lchild;     // 左孩子下标
    int rchild;     // 右孩子下标
} HTNode;

初始化时,前 n 个节点为叶子节点(字符+权重),parent 初始为 0;后续 n-1 个节点用于存储合并后的非叶节点。

示例代码(C语言风格):
void CreateHuffmanTree(HTNode ht[], int n) {
    int m = 2 * n - 1;
    for (int i = 1; i <= m; i++) {
        ht[i].parent = ht[i].lchild = ht[i].rchild = 0;
    }

    for (int k = n + 1; k <= m; k++) {
        int min1 = 9999, min2 = 9999;
        int x1 = 0, x2 = 0;

        // 找两个无父节点且权值最小的节点
        for (int j = 1; j < k; j++) {
            if (ht[j].parent == 0 && ht[j].weight < min1) {
                min2 = min1; x2 = x1;
                min1 = ht[j].weight; x1 = j;
            } else if (ht[j].parent == 0 && ht[j].weight < min2) {
                min2 = ht[j].weight; x2 = j;
            }
        }

        // 合并两个节点
        ht[k].weight = min1 + min2;
        ht[k].lchild = x1;
        ht[k].rchild = x2;
        ht[x1].parent = k;
        ht[x2].parent = k;
    }
}

二、生成哈夫曼编码表

原理:

从每个叶子节点出发,逆向回溯到根节点,路径上每一步:

  • 走左分支记为 '0'
  • 走右分支记为 '1'
    由于是从下往上生成,需将结果反转得到正确编码。
方法:逐个叶子节点遍历
#include <string.h>

void GenerateHuffmanCode(HTNode ht[], char hcode[][20], int n) {
    char temp[20];
    int top = 0;

    for (int i = 1; i <= n; i++) {
        int current = i;
        int parent = ht[i].parent;
        top = 0;

        // 从叶子向上遍历至根
        while (parent != 0) {
            if (ht[parent].lchild == current)
                temp[top++] = '0';
            else
                temp[top++] = '1';

            current = parent;
            parent = ht[parent].parent;
        }

        // 反转字符串并存入编码表
        temp[top] = '\0';
        for (int j = 0; j < top; j++) {
            hcode[i][j] = temp[top - 1 - j];
        }
        hcode[i][top] = '\0';
    }
}
编码表示方式:

使用二维字符数组 hcode[i][20] 存储第 i 个字符的哈夫曼编码。


完整流程示例(以字符频率 {‘A’:5, ‘B’:9, ‘C’:12, ‘D’:13, ‘E’:16, ‘F’:45} 为例)

字符频率哈夫曼编码
A51100
B91101
C12111
D13100
E16101
F450

注意:编码不唯一(左右子树可互换),但总带权路径长度 WPL 是最优的。


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值