【数据结构实验】哈夫曼树和哈夫曼代码

一、实验目的

  1. 掌握哈夫曼树的构造算法。
  2. 掌握哈夫曼编码的构造算法。

二、实验内容(题目)

输入一串字符串,根据给定的字符串中字符出现的频率建立相应哈夫曼树,构造哈夫曼编码表,在此基础上可以对待压缩文件进行压缩(即编码),同时可以对压缩后的二进制编码文件进行解压(即译码)。

三、实验环境

(1)软件系统
操作系统:Windows 10
编程环境:Embarcadero Dev-C++ 6.3,包含GNU GCC编译器和GDB调试器
(2)硬件系统
CPU:Intel® Core™ i7-10875H CPU @ 2.30GHz
内存:16GB
硬盘:500GB SSD

四、程序代码

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
using namespace std;

// 声明全局变量
int num;            // 不同字符的数量
int alpha[26];      // 字符频率数组
int num2 = 0;
int k = 0;          // 实际使用的数组长度
char list[26];      // 存储字符本身

// 哈夫曼树的节点结构
typedef struct {
    int weight;     // 节点的权重
    int parent, lchild, rchild; // 双亲和孩子节点的索引
} HTNode, *HuffmanTree; // 定义哈夫曼树

typedef char** HuffmanCode; // 定义哈夫曼编码类型

// 从哈夫曼树中选择两个权重最小且未被选择的节点
void Select(HuffmanTree HT, int sum, int &s1, int &s2)
{
    int i, min1 = INT_MAX, min2 = INT_MAX;
    for (i = 1; i <= sum; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight < min1)
        {
            min2 = min1;
            min1 = HT[i].weight;
            s2 = s1;
            s1 = i;
        }
        else if (HT[i].parent == 0 && HT[i].weight < min2)
        {
            min2 = HT[i].weight;
            s2 = i;
        }
    }
}

// 创建哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT, int n)
{
    if (n <= 1) return;
    int m = 2 * n - 1;
    HT = new HTNode[m + 1];
    for (int i = 1; i <= m; i++) {
        HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
    }
    for (int i = 1; i <= n; i++) // 初始化叶子节点的权重
        HT[i].weight = alpha[i - 1];
    int s1, s2;
    for (int i = n + 1; i <= m; i++) // 构建哈夫曼树
    {
        Select(HT, i - 1, s1, s2);
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].lchild = s1;
        HT[i].rchild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }
}

// 生成哈夫曼编码
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{
    HC = new char*[n + 1];
    char *cd = new char[n];
    cd[n - 1] = '\0';
    for (int i = 1; i <= n; i++)
    {
        int start = n - 1;
        for (int c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)
        {
            cd[--start] = (HT[f].lchild == c) ? '0' : '1';
        }
        HC[i] = new char[n - start];
        strcpy(HC[i], &cd[start]);
    }
    delete[] cd;
}

// 输出哈夫曼树和编码
void Output(HuffmanTree HT, HuffmanCode HC)
{
    for (int i = 1; i <= 2 * k - 1; i++)
        cout << i << " " << HT[i].weight << " " << HT[i].parent << " " << HT[i].lchild << " " << HT[i].rchild << endl;

    for (int i = 1; i <= k; i++)
    {
        cout << list[i - 1] << ":" << HC[i];
        if (i != k) cout << " ";
    }
    cout << endl;
}

// 初始化字符频率表
void Initalpha(int a[], string s)
{
    memset(a, 0, sizeof(int) * 26);
    for (char c : s) {
        if (islower(c)) a[c - 'a']++;
    }
    for (int i = 0; i < 26; i++) {
        if (a[i] != 0) {
            cout << char(i + 'a') << ":" << a[i] << " ";
            alpha[k] = a[i]; 
            list[k++] = char(i + 'a');
        }
    }
    cout << endl;
}

// 重置数据,为新的输入做准备
void InitData(int fre[])
{
    memset(fre, 0, sizeof(int) * 26);
    memset(list, 0, sizeof(list));
    k = num2 = num = 0;
}

// 将字符串翻译成哈夫曼编码
void Translate(HuffmanCode HC, string s)
{
    for (char c : s) {
        if (islower(c)) cout << HC[c - 'a' + 1];
    }
    cout << endl;
}

int main()
{
    string s;
    while (cin >> s && s != "0")
    {
        InitData(alpha);
        Initalpha(alpha, s);
        HuffmanTree HT;
        HuffmanCode HC;
        CreateHuffmanTree(HT, k);
        CreatHuffmanCode(HT, HC, k);
        Output(HT, HC);
        Translate(HC, s);
    }
    return 0;
}

五、实验分析

从以下方面分析:
1、正确性(输入的测试数据和测试结果,附测算截屏)
在提供的代码中,主要流程包括统计字符频率、构建哈夫曼树、生成哈夫曼编码、以及最后的编码输出和字符串翻译。函数正确统计输入中每个小写字母的出现次数。这是哈夫曼编码的基础,确保每个字符根据其出现频率被适当处理。通过连续选择两个最小权重的未选择节点,程序正确地构建了哈夫曼树。这保证了树的最优性,即常见字符有较短的路径。程序从叶节点到根节点反向追踪生成编码,确保了每个字符的哈夫曼编码是正确和唯一的。
测算截屏:在这里插入图片描述
2、可读性(主要在源代码中体现)
• 程序结构清晰,各函数功能明确,对主要的功能块有基本的注释。
• 使用了结构体和指针来管理哈夫曼树,增强了代码的模块化。
3、健壮性(容错性,主要在源代码中体现,在此简要说明)
• 优点:
• 程序能够持续处理多组输入直到遇到"0"。
• 缺点:
• 缺少对非预期输入(如大写字母、特殊字符等)的处理。
• 使用全局变量可能导致数据处理上的错误。
4、时间和空间复杂度(针对核心算法函数分析)
• 时间复杂度:整个程序的时间复杂度主要受哈夫曼树构建阶段的影响,大致为O(n2)。
• 空间复杂度:程序主要空间消耗来自于哈夫曼树和编码表的存储,总体空间复杂度为O(n),其中n是不同字符的数量。

实验结束

关注点赞收藏,获取更多干货内容~
本篇收录于数据结构专栏下,可前往首页查看更多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Frank牛蛙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值