首页 > 其他分享 >【数据结构实验】哈夫曼树和哈夫曼代码

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

时间:2024-06-02 10:58:33浏览次数:21  
标签:编码 weight 哈夫曼 int 代码 HT HC 数据结构

一、实验目的

  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是不同字符的数量。

实验结束

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

标签:编码,weight,哈夫曼,int,代码,HT,HC,数据结构
From: https://blog.csdn.net/weixin_64259675/article/details/139388240

相关文章

  • 代码随想录算法训练营第第25天 | 216.组合总和III 、17.电话号码的字母组合
    今天的题比较简单,重点是在于剪枝216.组合总和III如果把组合问题理解了,本题就容易一些了。题目链接/文章讲解:https://programmercarl.com/0216.组合总和III.html视频讲解:https://www.bilibili.com/video/BV1wg411873x/***@param{number}k*@param{number}n*@retu......
  • Transformer 模型完全解读:代码+注释+讲解
    节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。总结链接如下:重磅消息!《大模型面试......
  • 2024年武汉大学电信算法与数据结构期末复习随记
    期末复习易错点叶子结点以外的结点称为分支结点![img](file:///D:\qq消息记录\2844938982\nt_qq\nt_data\Pic\2024-06\Ori\9d5f4aefd34e1d8587152f79b567d05a.jpeg)时间复杂度![img](file:///D:\qq消息记录\2844938982\nt_qq\nt_data\Pic\2024-05\Ori\4cb6f5297e5f4c3c977d0e......
  • 消消乐_中级代码
    本次消消乐会预先提供五个游戏地图,分别表示五关。五关的通关条件分别为:消除⾄少6个A消除⾄少6个B消除⾄少3个B和⾄少3个C总共消除9个元素(元素类型可以不同)有两次消除操作满⾜:消除掉的元素个数⼤于5个(元素类型可以不同)实现的功能:功能1游戏玩家识......
  • 【代码片段】使用docker部署nginx 并通过nginx设置密码访问控制
    使用docker部署nginx服务docker-compose.ymlversion:'3'services:web:image:nginxvolumes:-./nginx.conf:/etc/nginx/nginx.confrestart:alwaysports:-"80:80"-"443:443"environment:......
  • Redis笔记——底层数据结构之压缩列表
    是什么?        本质上就是紧凑的列表。        压缩列表在Redis中有两种编码方式,分别是ZIPLIST与LISTTPACK。LISTPACK从Redis5.0引入,直至Redis7.0完全替换了ZIPLIST,可以看作是ZIPLIST的进阶版。有什么作用?        在List文章中,提......
  • 深入解析力扣170题:两数之和 III - 数据结构设计(哈希表与双指针法详解及模拟面试问答)
    在本篇文章中,我们将详细解读力扣第170题“两数之和III-数据结构设计”。通过学习本篇文章,读者将掌握如何设计一个数据结构来支持两种操作,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释和ASCII图解,以便于理解。问题描述力扣第170题“两数之和III......
  • 改进rust代码的35种具体方法-类型(二十)-避免过度优化的诱惑
    上一篇文章-改进rust代码的35种具体方法-类型(十九)-避免使用反射“仅仅因为Rust允许您安全地编写超酷的非分配零复制算法,并不意味着您编写的每个算法都应该是超级酷的、零复制和非分配的。”-trentj   这本书中的大多数项目都旨在帮助现有程序员熟悉Rust及其成语。......
  • Java中的Lambda表达式与函数式接口:简化代码与提升效率
            Lambda表达式自Java8引入以来,已成为Java编程中提高代码简洁性与效率的一种重要特性。Lambda表达式允许你以匿名函数的方式来编写方法,使代码更简洁,增强了集合库的功能,尤其是在处理集合操作时。本文将探讨Lambda表达式的基本概念、函数式接口的用途,以及如何在实......
  • 什么!程序员不乖乖写代码,跑去写小说了?一时兴起写了《雪中悍刀行》的番外,请品鉴!
    写在开头  什么!程序员不乖乖写代码,跑去写小说了?哈哈,没错!build哥一时兴起写了篇《雪中悍刀行》的番外,是关于剑九黄的,请诸君品鉴!(第一次写,喷轻点呀)  build哥除了写代码之外,日常生活中挺喜欢看小说的,尤其是烽火戏诸侯的《雪中悍刀行》,可谓大爱,几乎每晚睡觉前必看。不过,这部小......