C++Qt6 哈夫曼编码求解 数据结构课程设计 | JorbanS

news2024/11/18 9:20:56

一、 问题描述

在进行程序设计时,通常给每一个字符标记一个单独的代码来表示一组字符,即编码。在进行二进制编码时,假设所有的代码都等长,那么表示 n 个不同的字符需要 位,称为等长编码。如果每个字符的使用频率相等,那么等长编码无疑是空间效率最高的编码函数,而如果字符出现的频率不同,则可以让频率高的字符采用尽可能短的编码,频率低的字符采用尽可能长的编码,来构造出一种不等长编码,从而获得更好的空间效率。
在设计不等长编码时,要考虑解码的唯一性,如果一组编码中任一编码都不是其他任何一个编码的前缀,那么称这组编码为前缀编码,其保证了编码被解码时的唯一性。霍夫曼树可用于构造最短的前缀编码,即哈夫曼编码(Huffman Code)。
—— OI Wiki

二、 设计思路

以下是项目目录结构图:

HuffmanTree/
├── include/
│   ├── HuffmanNode.h
│   └── HuffmanTree.h
├── src/
│   ├── HuffmanNode.cpp
│   └── HuffmanTree.cpp
├── mainwindow.h
├── mainwindow.ui
├── mainwindow.cpp
└── main.cpp
  1. 创建 HuffmanNode 类:
    HuffmanNode 类用于表示 Huffman 树的节点。
    每个节点包含字符数据、频率以及左右子节点指针。
    为了便于操作,该类包括一个构造函数,用于初始化节点的数据成员。
  2. 创建 CompareNodes 结构体:
    CompareNodes 结构体是一个自定义的比较器。
    用于在优先队列中比较两个 HuffmanNode 指针的频率,以便按频率升序排序节点。
  3. 创建 HuffmanTree 类:
    HuffmanTree 类表示 Huffman 树的结构和操作。
    构造函数接受一个映射(map)参数,其中包含字符和对应的频率,然后调用 buildTree 函数构建 Huffman 树。
  4. 实现 buildTree 函数:
    buildTree 函数用于根据字符频率构建 Huffman 树。
    使用优先队列(最小堆)来维护节点,每个节点代表一个字符及其频率。
    循环从优先队列中弹出两个最小频率的节点,然后创建一个新的父节点,将这两个节点作为子节点,父节点的频率为子节点频率之和。
    将新创建的父节点放回优先队列,重复这个过程直到队列中只剩下一个节点,即 Huffman 树的根节点。
  5. 实现 generateCodes 函数:
    generateCodes 函数是一个递归函数,用于遍历 Huffman 树并生成字符的 Huffman 编码。
    从根节点开始,沿着左子树的路径添加 “0”,沿着右子树的路径添加 “1”。
    当遇到叶子节点时,将字符和路径编码存储在一个字符到编码的映射中。

三、 数据结构设计

  1. 哈夫曼节点类 HuffmanNode:
    HuffmanNode 类用于表示哈夫曼树的节点,包括字符数据、频率以及左右子节点指针。
    构造函数初始化节点的数据、频率和子节点指针。
  2. 比较器结构 CompareNodes:
    CompareNodes 结构用于比较两个 HuffmanNode 指针,按照频率从小到大的顺序排序,以便在优先队列中使用。
  3. 哈夫曼树类 HuffmanTree:
    HuffmanTree 类用于构建哈夫曼树。
    构造函数接受一个 map<char, int> 参数,其中包含字符和它们的频率信息,并调用 buildTree 函数构建哈夫曼树。
    buildTree 使用优先队列(最小堆)来构建哈夫曼树,首先将字符频率转化为节点,然后反复合并频率最低的两个节点,直到只剩下一个根节点。
    getCodes 函数生成字符到编码的映射。
  4. 私有函数 generateCodes:
    generateCodes 用于递归地生成字符到编码的映射,遍历哈夫曼树,构建每个字符的编码。
    在遇到叶子节点时将字符和编码存储在映射中。

四、 功能函数设计

  1. 包含头文件和命名空间:代码包含了 、 和 头文件,并使用 using namespace std; 来引入标准命名空间。
  2. 用于比较 HuffmanNode 指针的比较器 CompareNodes:定义了一个用于优先队列比较的自定义比较器,按照节点的频率从小到大进行比较。
  3. buildTree 构建哈夫曼树的主要逻辑,使用优先队列来合并频率最低的节点,直到只剩下一个根节点。
  4. getCodes 用于获取字符到哈夫曼编码的映射。
  5. generateCodes:递归,用于生成字符到哈夫曼编码的映射,从根节点开始遍历树。

五、 程序代码

// main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("DS 课程设计 zdy229074447");
    w.show();
    return a.exec();
}
// mainwindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtWidgets>
#include <map>
#include "include/HuffmanTree.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow: public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void onButtonConfirmClicked();
    void onButtonResetClicked();
private:
    Ui::MainWindow *ui;
    QTableWidget* tableWidget;
    QLineEdit* charInput;
    QLineEdit* frequencyInput;
    QPushButton* buttonConfirm, *buttonReset;
    HuffmanTree* huffmanTree;
    map<char, int> frequencies;
    void updateTable();
};

#endif // MAINWINDOW_H
// mainwindow.cpp

#include <QtWidgets>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "include/HuffmanNode.h"
#include "include/HuffmanTree.h"

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    // 初始化输入框和按钮
    tableWidget = this->findChild<QTableWidget *>("Table_Result");
    charInput = this->findChild<QLineEdit *>("Char_Input");
    frequencyInput = this->findChild<QLineEdit *>("Frequency_Input");
    buttonConfirm = this->findChild<QPushButton *>("Button_Confirm");
    buttonReset = this->findChild<QPushButton *>("Button_Reset");

    // 连接按钮点击事件到槽函数Z
    connect(buttonConfirm, &QPushButton::clicked, this, &MainWindow::onButtonConfirmClicked);
    connect(buttonReset, &QPushButton::clicked, this, &MainWindow::onButtonResetClicked);

    // 初始化哈夫曼树
    huffmanTree = nullptr; // 在这里初始化为空

    // 设置表格的列数和表头
    tableWidget->setColumnCount(3);
    tableWidget->setHorizontalHeaderLabels({"字符", "频次", "哈夫曼编码"});

}

void MainWindow::onButtonResetClicked() {
    for (auto pair: frequencies) {
        frequencies[pair.first] = 0;
    }
    delete huffmanTree;
    huffmanTree = new HuffmanTree(frequencies);
    updateTable();
}

void MainWindow::onButtonConfirmClicked() {
    QString charText = charInput->text();
    QString freqText = frequencyInput->text();

    if (!charText.isEmpty() && !freqText.isEmpty()) {
        char character = charText.at(0).toLatin1(); // 获取第一个字符
        int frequency = freqText.toInt();
        // 创建哈夫曼树(如果尚未创建)
        if (huffmanTree == nullptr) {
            frequencies[character] = frequency; // 使用 [] 操作符添加键值对
            huffmanTree = new HuffmanTree(frequencies);
        } else {
            // 更新频率
            frequencies[character] += frequency;
            huffmanTree->buildTree(frequencies); // 重新构建哈夫曼树
        }

        // 更新表格内容
        updateTable();
    }
}


void MainWindow::updateTable() {
    // 清空表格内容
    tableWidget->clearContents();
    tableWidget->setRowCount(0);

    // 获取哈夫曼树的编码映射
    map<char, string> codes = huffmanTree->getCodes();

    // 遍历编码映射并更新表格
    int row = 0;
    for (auto pair: codes) {
        tableWidget->insertRow(row);
        QTableWidgetItem* charItem = new QTableWidgetItem(QString(pair.first));
        QTableWidgetItem* frequencyItem = new QTableWidgetItem(QString::number(frequencies[pair.first]));
        QTableWidgetItem* codeItem = new QTableWidgetItem(QString::fromStdString(pair.second));
        tableWidget->setItem(row, 0, charItem);
        tableWidget->setItem(row, 1, frequencyItem);
        tableWidget->setItem(row, 2, codeItem);
        row++;
    }
}

MainWindow::~MainWindow() {
    delete ui;
}
// HuffmanNode.h

#ifndef HUFFMANNODE_H
#define HUFFMANNODE_H

class HuffmanNode {
public:
    char data;
    int frequency;
    HuffmanNode *left, *right;

    HuffmanNode(char data, int frequency);
};

#endif // HUFFMANNODE_H
// HuffmanTree.h

#ifndef HUFFMANTREE_H
#define HUFFMANTREE_H

#include "HuffmanNode.h"
#include <map>
#include <string>

using namespace std;

// 哈夫曼树类
class HuffmanTree {
private:
public:
    void generateCodes(HuffmanNode* node, string code, map<char, string>& codes);
    HuffmanNode* root;
    HuffmanTree(map<char, int> frequencies);
    void buildTree(map<char, int> frequencies);
    map<char, string> getCodes();
};

#endif // HUFFMANTREE_H
// HuffmanNode.cpp

#include "../include/HuffmanNode.h"

HuffmanNode::HuffmanNode(char data, int frequency) {
    this->data = data;
    this->frequency = frequency;
    this->left = nullptr;
    this->right = nullptr;
}
// HuffmanTree.cpp

#include "../include/HuffmanTree.h"
#include <vector>
#include <queue>

// 用于比较 HuffmanNode 指针的比较器
struct CompareNodes {
    bool operator()(HuffmanNode* left, HuffmanNode* right) {
        return left->frequency > right->frequency;
    }
};

HuffmanTree::HuffmanTree(map<char, int> frequencies) {
    buildTree(frequencies);
}

void HuffmanTree::buildTree(map<char, int> frequencies) {
    priority_queue<HuffmanNode*, vector<HuffmanNode*>, CompareNodes> heap;

    // 将字符频率构建为节点,并将它们添加到优先队列
    for (auto entry: frequencies) {
        HuffmanNode* node = new HuffmanNode(entry.first, entry.second);
        heap.push(node);
    }

    // 构建哈夫曼树
    while (heap.size() > 1) {
        HuffmanNode* left = heap.top();
        heap.pop();
        HuffmanNode* right = heap.top();
        heap.pop();

        // 创建一个新节点作为父节点,其频率为左右子树的频率之和
        HuffmanNode* parent = new HuffmanNode('\0', left->frequency + right->frequency);
        parent->left = left;
        parent->right = right;

        heap.push(parent);
    }

    // 根节点就是哈夫曼树的根
    root = heap.top();
}

map<char, string> HuffmanTree::getCodes() {
    map<char, string> codes;
    generateCodes(root, "", codes);
    return codes;
}

void HuffmanTree::generateCodes(HuffmanNode* node, string code, map<char, string>& codes) {
    if (node == nullptr) return;

    // 叶子节点,将字符和编码存储在映射中
    if (node->data != '\0') codes[node->data] = code;

    generateCodes(node->left, code + "0", codes);
    generateCodes(node->right, code + "1", codes);
}
// mainwindows.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>451</width>
    <height>582</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="Button_Confirm">
    <property name="geometry">
     <rect>
      <x>260</x>
      <y>10</y>
      <width>111</width>
      <height>41</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">font: 10pt &quot;Microsoft YaHei UI&quot;;</string>
    </property>
    <property name="text">
     <string>添加</string>
    </property>
   </widget>
   <widget class="QLineEdit" name="Char_Input">
    <property name="geometry">
     <rect>
      <x>50</x>
      <y>10</y>
      <width>51</width>
      <height>41</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">font: 10pt &quot;Microsoft YaHei UI&quot;;</string>
    </property>
    <property name="text">
     <string/>
    </property>
   </widget>
   <widget class="QLineEdit" name="Frequency_Input">
    <property name="geometry">
     <rect>
      <x>140</x>
      <y>10</y>
      <width>111</width>
      <height>41</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">font: 10pt &quot;Microsoft YaHei UI&quot;;</string>
    </property>
    <property name="text">
     <string/>
    </property>
   </widget>
   <widget class="QTableWidget" name="Table_Result">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>60</y>
      <width>411</width>
      <height>471</height>
     </rect>
    </property>
   </widget>
   <widget class="QPushButton" name="Button_Reset">
    <property name="geometry">
     <rect>
      <x>380</x>
      <y>10</y>
      <width>51</width>
      <height>41</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">font: 10pt &quot;Microsoft YaHei UI&quot;;</string>
    </property>
    <property name="text">
     <string>重置</string>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>20</y>
      <width>41</width>
      <height>19</height>
     </rect>
    </property>
    <property name="text">
     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;字符&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>20</y>
      <width>41</width>
      <height>19</height>
     </rect>
    </property>
    <property name="text">
     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;频次&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
    </property>
   </widget>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>451</width>
     <height>24</height>
    </rect>
   </property>
   <widget class="QMenu" name="menu">
    <property name="title">
     <string>哈夫曼编码求解</string>
    </property>
   </widget>
   <addaction name="menu"/>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

六、 运行结果及分析

在这里插入图片描述

多次添加后,如左图;重置操作后,如右图
所需实现的功能达成,并且可以动态更新。

七、 设计心得

使用了C++的面向对象编程来将Huffman编码的各个组成部分封装成类,这有助于代码的模块化和可维护性。合理地使用了STL容器和数据结构,如map和priority_queue,来简化实现。通过C++Qt构建图形化GUI界面,简化了用户操作,提升了交互属性。

八、 参考资料

霍夫曼树 - OI Wiki (oi-wiki.org) https://oi-wiki.org/ds/huffman-tree/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1352138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系列七、Ribbon

一、Ribbon 1.1、概述 Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具&#xff0c;是Netflix发布的一款开源项目&#xff0c;其主要功能是提供客户端的软件负载均衡算法和服务调用&#xff0c;Ribbon客户端组件提供一系列完善的配置项&#xff0c;例如&#xff1a…

免费证书Let’s Encrypt

免费SSL证书是一种用于保护网站数据传输安全的加密技术。它能够确保用户与网站之间的信息传输是加密的&#xff0c;防止被黑客窃取或篡改。随着网络安全意识的提高&#xff0c;越来越多的网站开始使用SSL证书来保护用户的隐私和数据安全。 在过去&#xff0c;SSL证书需要购买才…

OpenAI官方发布ChatGPT 提示词指南,六大策略让你玩转大语言模型!

OpenAI前段时间官方放出了自己的提示工程指南&#xff0c;从大模型小白到开发者&#xff0c;都可以从中消化出不少营养。看看全世界最懂大模型的人&#xff0c;是怎么写提示词的。官方给出了6 个大提示策略&#xff1a; 1、清晰的指令&#xff1a; 告诉AI你具体想要什么。比如…

ElasticSearch的DSL查询语法解析

Elasticsearch提供了基于ISON的DSL (Domain Specific Lanquage)来定义查询。 目录 一、常见查询类型 二、DSLQuery基本语法 三、全文检索查询 3.1 match查询&#xff1a;会对用户输入内容分词&#xff0c;常用于搜索框搜索 &#xff0c;语法&#xff1a; 3.2 multi match…

RK3568 学习笔记 : 解决 linux_sdk 编译 python 版本报错问题

前言 最近买了 【正点原子】 的 RK3568 开发板&#xff0c;下载了 开发板的资料&#xff0c;包括 Linux SDK&#xff0c;这个 Linux SDK 占用的空间比较大&#xff0c;扩展了一下 VM 虚拟机 ubuntu 20.04 的硬盘空间&#xff0c;编译才正常通过。 编译 RK3568 Linux SDK 时&am…

STM32存储左右互搏 SPI总线读写FRAM MB85RS2M

STM32存储左右互搏 SPI总线读写FRAM MB85RS2M 在中低容量存储领域&#xff0c;除了FLASH的使用&#xff0c;&#xff0c;还有铁电存储器FRAM的使用&#xff0c;相对于FLASH&#xff0c;FRAM写操作时不需要预擦除&#xff0c;所以执行写操作时可以达到更高的速度&#xff0c;其…

Postgresql源码(119)PL/pgSQL中ExprContext的生命周期

前言 在PL/pgSQL语言中&#xff0c;执行任何SQL都需要通过SPI调用SQL层解析执行&#xff0c;例如在SQL层执行表达式的入口&#xff1a; static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,PLpgSQL_expr *expr,Datum *result,bool *isNull,Oid *rettype,int32 *re…

RK3568驱动指南|第九篇 设备模型-第95章 创建属性文件并实现读写功能实验1

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

《PySpark大数据分析实战》-23.Pandas介绍DataFrame介绍

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

STM32H5XX和STM32H7XX选型对比

文章目录 STM32H563/H573STM32H743/753对比内核不同H5独有安全管理器H7的外设资源更丰富 STM32H563/H573 STM32H563和STM32H573微控制器扩展了STM32高性能产品组合。这两款微控制器具有增强的性能和安全性、更高的能效和更多的片内外设。 STM32H563/573产品系列提供1至2 MB的…

ChatGPT 4.0真的值得花钱买入吗?

性能提升&#xff1a; ChatGPT 4.0的推出不仅意味着更先进的技术&#xff0c;还代表着更强大的性能。相较于3.5&#xff0c;4.0在处理任务时更为高效&#xff0c;响应更迅速。 更智能的理解&#xff1a; 随着版本的升级&#xff0c;ChatGPT 4.0对语境的理解能力得到了进一步的…

Unity游戏资源更新(AB包)

目录 前言&#xff1a; 一、什么是AssetBundle 二、AssetBudle的基本使用 1.AssetBundle打包 2.BuildAssetBundle BuildAssetBundleOptions BuildTarget 示例 3.AssetBundle的加载 LoadFromFile LoadFromMemory LoadFromMemoryAsync UnityWebRequestAsssetBundle 前…

集群部署篇--Redis 集群动态伸缩

文章目录 前言一、redis 节点的添加1.1 redis 的实例部署&#xff1a;1.2 redis 节点添加&#xff1a;1.3 槽位分配&#xff1a;1.4 添加从节点&#xff1a; 二、redis 节点的减少2.1 移除主节点2.1.1 迁移槽位2.1.1 删除节点&#xff1a; 三、redis 删除节点的重新加入3.1 加入…

【数据不完整?用EM算法填补缺失】期望值最大化 EM 算法:睹始知终

期望值最大化算法 EM&#xff1a;睹始知终 算法思想算法推导算法流程E步骤&#xff1a;期望M步骤&#xff1a;最大化陷入局部最优的原因 算法应用高斯混合模型&#xff08;Gaussian Mixture Model, GMM&#xff09;问题描述输入输出Python代码实现 算法思想 期望值最大化方法&a…

odoo17 | 用户界面的基本交互

前言 现在我们已经创建了我们的新模型及其 相应的访问权限&#xff0c;是时候了 与用户界面交互。 在本章结束时&#xff0c;我们将创建几个菜单以访问默认列表 和窗体视图。 数据文件 &#xff08;XML&#xff09; Odoo在很大程度上是数据驱动的&#xff0c;因此模块定义的…

桌面天气预报软件 Weather Widget free mac特点介绍

Weather Widget free for Mac多种吸引人的小部件设计可供选择&#xff0c;可以随时了解天气&#xff01;还可以在Dock和菜单栏中为您提供简短的天气预报或当前状况的概述。 Weather Widget free for Mac软件介绍 始终在桌面上使用时尚的天气小部件来随时了解天气&#xff01;多…

团结引擎正式发布,已开放下载

2024年1月1日&#xff0c;Unity 正式发布了针对中国开发者的引擎--团结引擎创世版&#xff0c;现在已经可以开放下载&#xff0c;想体验的小伙伴们可以到官网下载了。 团结引擎是专为中国开发者定制的实时3D引擎&#xff0c;目前是基于Unity 2022LTS版本开发的。团结引擎内置了…

我用 Python 自动生成图文并茂的数据分析报告

reportlab是Python的一个标准库&#xff0c;可以画图、画表格、编辑文字&#xff0c;最后可以输出PDF格式。它的逻辑和编辑一个word文档或者PPT很像。有两种方法&#xff1a; 1&#xff09;建立一个空白文档&#xff0c;然后在上面写文字、画图等&#xff1b; 2&#xff09;建…

24、Web攻防——通用漏洞SQL注入MYSQL跨库ACCESS偏移

文章目录 一、SQL注入原理   脚本代码在与数据库进行数据通讯时&#xff08;从数据库取出相关数据进行页面显示&#xff09;&#xff0c;使用预定义的SQL查询语句进行数据查询。能通过参数传递自定义值来实现SQL语句的控制&#xff0c;执行恶意的查询操作&#xff0c;例如查询…

Kali Linux实现UEFI和传统BIOS(Legacy)引导启动

默认Kali linux安装会根据当前启动的引导模式进行安装 例:以UEFI引导启动安装程序,安装后仅能在UEFI引导模式下进入系统 安装Kali系统 这边基于VirtualBox虚拟机镜像实战操作 首先创建一个Kali虚拟机 这里需要注意,把启动 EFI (只针对某些操作系统)选项勾选上,内存、处理器…