目录
一、 概述
1.1 前言
1.2 功能
二、代码实现
2.1 哈夫曼树类
huffmanTree.h
huffmanTree.cpp
2.2 使用介绍
三、使用Qt进行窗口化
3.1 Qt 安装
3.2 Qt 基本使用
3.3 Qt编程
3.3.1 窗口设置
3.3.2 按钮按下函数
3.4 项目打包
大一的课程已经快结束了,临近期末,数据结构与算法的课设也开始了,我的课设题目是哈夫曼树,最基本的要求是能够构建哈夫曼树并输出密文与密匙,当然既然决定要好好完成就不能这么敷衍,下面我来分享我的课设。
1.要有图形化
2.可以进行文件输出
3.可以通过文件读取密文、密匙进行解译
这里面的 ui_mainwindow.h Qt中的头文件,在后面使用Qt时我会再次提到。
#ifndef HUFFMANTREE_H
#define HUFFMANTREE_H
#include
#include
#include
#include
#include "ui_mainwindow.h" //qt窗口头文件
#define ASCII_MAX 128 //ASCII字符最大数量
#define DEFAULT "default"
using namespace std;
/*
设计思路:
|1、检验文本,先将字符数组放入文本数据数组中,将文本字符以哈夫曼树节点放入链表 treeNodeList 中
|2、使用递归的方法将哈夫曼树节点链表中的节点合并为一个哈夫曼树,此时哈夫
| 曼节点链表只有一个节点
|3、访问叶子以得到哈夫曼码
*/
//哈夫曼树节点结构体
typedef struct HuffmanNode
{
int weight = 0; //权值
char dat = 0; //字符
bool haveFind = false; //被寻找过的标志
HuffmanNode* lchild = nullptr;
HuffmanNode* rchild = nullptr;
}HuffmanNode;
//哈夫曼密钥结构体
typedef struct HuffmanSecretKey {
char ch = 0;
string key = "";
}HuffmanSecretKey;
//哈夫曼树类
class HuffmanTree
{
private:
HuffmanNode* huffmanTree = nullptr;//创建出来的哈夫曼树,在创建完成之前它为空值
listtreeNodeList;//树节点链表
string text;//明文
string secretText;//密文
HuffmanNode textData[ASCII_MAX];//文本字符数据数组 - 第一位是判断密钥是否为空的标志位:ch: 0 - 空 1- 非空
HuffmanSecretKey key[ASCII_MAX + 1];//哈夫曼密钥
public:
HuffmanTree();//哈夫曼树初始化函数
void treeNodeListConstruct(); //树节点链表构造器
void setText(string text);//设置明文函数
void huffmanTreeInit(); //哈夫曼树初始化函数
void treeCreat(); //哈夫曼树创建函数
void creatSecretKey();//哈夫曼密钥创建函数
string creatSecretText();//创建密文函数
string huffmanDecode();//哈夫曼解密函数
void writeSecretKeyToFile(string path); //文件存储密匙函数
void writeSecretTextToFile(string path);//文件储存密文函数
void getSecretKey(string path);//获取外部密钥函数
void getSecretText(string path);//获取外部密文函数
bool keyIsEmply();//密文是否为空
private:
HuffmanNode* _findSmallWeight();//找到树节点链表中树根权值最小树
void _creatSecretKey(HuffmanNode* node,string str,char turn);//储存哈夫曼密钥的执行函数
//qt与哈夫曼相关函数
public:
void printTextToTextEdit(QPlainTextEdit* textEdit,string status); //打印明文函数
void printSecretKeyToTextEdit(QPlainTextEdit* textEdit); //打印密钥函数
void printSecretTextToTextEdit(QPlainTextEdit* textEdtt); //打印密钥函数
};
#endif // !HUFFMANTREE_H
里面有详细注释,所以读者阅读应该也很方便
#include "huffmanTree.h"
/*********************************************************
函数名称:哈夫曼树构造器函数
函数参数:void - 无
返回类型:void - 无
函数介绍:初始化文本数据数组,方便对要解码的数据存储,牺牲
空间省时间属实了是
**********************************************************/
HuffmanTree::HuffmanTree()
{
this->huffmanTreeInit();
}
/*********************************************************
函数名称:哈夫曼树初始化函数
函数参数:void - 无
返回类型:void - 无
函数介绍:初始化哈夫曼树对象,每次生成树都应调用该函数
**********************************************************/
void HuffmanTree::huffmanTreeInit()
{
//初始化哈夫曼树指针
this->huffmanTree = nullptr;
//初始化树节点链表
this->treeNodeList.clear();
//初始化明文串
this->text = "";
//初始化密文串
this->secretText = "";
//初始化文本字符数据,方便数据处理
for (int i = 0; i < ASCII_MAX; i++)
{
this->textData[i].dat = i;
this->textData[i].weight = 0;
this->textData[i].lchild = nullptr;
this->textData[i].rchild = nullptr;
this->textData[i].haveFind = false;
}
//初始化密匙数组
this->key[0].ch = '0';
for (int i = 0; i < ASCII_MAX; i++)
{
this->key[i + 1].ch = i;
this->key[i + 1].key = "";
}
}
/*********************************************************
函数名称:哈夫曼树节点构造器
函数参数:string text - 需要进行哈夫曼编码的文本(ASCII),
最好不要超过1000字符
返回类型:void - 无
函数介绍:将需要进行哈夫曼编码的文本进行预处理,方便进行哈
夫曼树的创建
*********************************************************/
void HuffmanTree::treeNodeListConstruct()
{
//将文本中的字符加权整理
for (int i = 0; i < this->text.length(); i++)
{
char ch = this->text.at(i);
this->textData[ch].weight++; //该节点权重增加
}
//存入链表 - 存入条件:权值不为0
for (int i = 0; i < ASCII_MAX; i++)
{
if (this->textData[i].weight != 0)
{
//复制这个点
HuffmanNode* newNode = new HuffmanNode;
newNode->dat = this->textData[i].dat;
newNode->weight = this->textData[i].weight;
//存入树节点链表
treeNodeList.push_back(newNode);
}
else continue;
}
}
/*********************************************************
函数名称:设置明文函数
函数参数:string text - 明文
返回类型:void - 无
函数介绍:将明文储存在哈夫曼树中
*********************************************************/
void HuffmanTree::setText(string text)
{
this->text = text;
}
/*********************************************************
函数名称:哈夫曼树创建函数
函数参数:void - 无
返回类型:void - 无
函数介绍:通过递归来实现哈夫曼树的创建
| 递归停止条件:当树节点链表中只有一颗树时停止
| 递归部分
| 1、找到两个树根权值最小的树
| 2、新建一个节点,让这两个根权值最小的树
| 成为新树根的左孩子、右孩子(习惯上以
| 权最小的树为左孩子),它的权为两孩子
| 节点权的和,然后在链表中删除左右孩子
| 3、递归调用
*********************************************************/
void HuffmanTree::treeCreat()
{
if (treeNodeList.size() == 1)
{
this->huffmanTree = this->treeNodeList.back();
return;
}
else
{
//找到权值最小的两个字符的树
HuffmanNode* lowestWeight = this->_findSmallWeight();
HuffmanNode* lowerWeight = this->_findSmallWeight();
//合并后加入树节点链表
HuffmanNode* newNode = new HuffmanNode;
newNode->weight = lowestWeight->weight + lowerWeight->weight;
newNode->lchild = lowestWeight;
newNode->rchild = lowerWeight;
treeNodeList.push_back(newNode);
//删除两权树
treeNodeList.remove(lowestWeight);
treeNodeList.remove(lowerWeight);
//递归调用
this->treeCreat();
}
}
/*********************************************************
函数名称:最小权根寻找函数
函数参数:void - 无
返回类型:HuffmanNode* tree - 根的权最小的树
函数介绍:找到树节点链表中树根权值最小的树,如多次调用会依
此返回权值较小的树
*********************************************************/
HuffmanNode* HuffmanTree::_findSmallWeight()
{
//要返回的树
HuffmanNode* tree = new HuffmanNode;
//创建链表迭代器
list::iterator it;
//让tree指向链表第一个未被标记的树
for (it = treeNodeList.begin(); it != treeNodeList.end();it++)
{
if ((*it)->haveFind == false)
{
tree = *it;
break;
}
}
//让tree指向比它权值还小的树
for (it = treeNodeList.begin(); it != treeNodeList.end(); it++)
{
if ((*it)->weight < tree->weight && (*it)->haveFind == false)
tree = (*it);
}
//这颗树已被寻找
tree->haveFind = true;
//返回
return tree;
}
/*********************************************************
函数名称:哈夫曼密钥存储函数(公有)
函数参数:void - 无
返回类型:void - 无
函数介绍:如果哈夫曼树存在,将哈夫曼树的编码存储在哈夫曼编
码数组中,这是一个公用方法,它是其私有方法的调用
方法
*********************************************************/
void HuffmanTree::creatSecretKey()
{
if (this->huffmanTree == nullptr)
return;
key[0].ch = '1';
string str;
_creatSecretKey(this->huffmanTree, str, 'N');
}
/*********************************************************
函数名称:哈夫曼密钥存储函数(私有)
函数参数:HuffmanNode* node - 访问到的节点
string str - 零时密匙的储存串
char turn - 访问方向(左:L,右:R)
返回类型:void - 无
函数介绍:如果哈夫曼树存在,将哈夫曼树的编码存储在哈夫曼编
码数组中,这是一个私有方法,它是其公有方法的实现
方法
*********************************************************/
void HuffmanTree::_creatSecretKey(HuffmanNode* node,string str,char turn)
{
//创建路径
if (turn == 'L')
str = str + "0";
if (turn == 'R')
str = str + "1";
//寻找叶子节点
if (node->lchild == nullptr && node->rchild == nullptr)
{
this->key[node->dat + 1].ch = node->dat;
this->key[node->dat + 1].key = str;
return;
}
//递归调用
_creatSecretKey(node->lchild, str, 'L');
_creatSecretKey(node->rchild, str, 'R');
}
/*********************************************************
函数名称:创建密文函数
函数参数:void - 无
返回类型:string - 密文
函数介绍:当明文存在时,将明文转换为哈夫曼密文
*********************************************************/
string HuffmanTree::creatSecretText()
{
if (this->text != "")
{
string secreText;
for (int i = 0; i < text.size(); i++)
{
secreText += key[text.at(i) + 1].key;
}
this->secretText = secreText;
return secreText;
}
return "null text";
}
/*********************************************************
函数名称: 文件储存密匙函数
函数参数:string path - 要创建储存密匙的文件路径,若传入DEFAULT
则为默认路径
返回类型:void - 无
函数介绍:将密匙以文件形式储存
**********************************************************/
void HuffmanTree::writeSecretKeyToFile(string path)
{
if (path == "default")
{
path = "secretKey.txt";
}
//声明ofstream对象
ofstream fp;
//打开文件
fp.open(path);
//写入
if(this->key[0].ch == '0')//密钥不存在
{
fp << "null text" << endl;
fp.close();
return;
}
for (int i = 0; i < ASCII_MAX; i++)//密钥存在
{
if (key[i + 1].key.size() == 0)
continue;
fp << '\''
<< (char)(i)
<< '\''
<< ':'
<< this->key[i + 1].key
<< endl;
}
//关闭文件
fp.close();
}
/*********************************************************
函数名称: 文件储存密文函数
函数参数:string path - 要创建储存密文的文件路径,若传入DEFAULT
则为默认路径
返回类型:void - 无
函数介绍:将密文以文件形式储存
**********************************************************/
void HuffmanTree::writeSecretTextToFile(string path)
{
if (path == DEFAULT)
{
path = "secretText.txt";
}
//声明ofstream对象
ofstream fp;
//打开文件
fp.open(path);
//写入
fp<creatSecretText();
//关闭文件
fp.close();
}
/*********************************************************
函数名称:哈夫曼解密函数
函数参数:string secretText - 密文串
返回类型:string text - 解译出来的文本或者解译错误文本
函数介绍:将密文解密,若密文或者密匙错误均会导致解译错误从而
返回错误文本
**********************************************************/
string HuffmanTree::huffmanDecode()
{
string text;
//判断密匙是否为空
if (this->key[0].ch == '0')
return "ERROR : The key is empty";
while (this->secretText.size())
{
//原始密文长度
int flag = this->secretText.size();
//解密一个字符
for (int i = 0; i < ASCII_MAX; i++)
{
//空字符
if (this->key[i + 1].key == "")
continue;
//解密成功
if (this->key[i + 1].key == this->secretText.substr(0, this->key[i + 1].key.size()))
{
text += this->key[i + 1].ch;
this->secretText.erase(0, this->key[i + 1].key.size());
break;
}
}
//若原始密文长度与解密一个字符后的长度相等,即为解密不成功
if (flag == secretText.size())
{
return "ERROR : Incorrect ciphertext or key";
}
}
//解密完成
this->text = text;
return "Success";
}
/*********************************************************
函数名称:获取外部密钥函数
函数参数:string path - 密钥文件路径,若传入DEFAULT则为默认
路径
返回类型:void - 无
函数介绍:获取外部密匙,此操作会替换原密钥
**********************************************************/
void HuffmanTree::getSecretKey(string path)
{
//key并未初始化
if (key[0].ch == '1')
{
for (int i = 0; i < ASCII_MAX; i++)
{
this->key[i + 1].key = "";
}
}
//为key添加标志
if (key[0].ch == '0')
{
key[0].ch = '1';
}
//设置路径初始值
if (path == DEFAULT)
{
path = "secretKey.txt";
}
//打开文件
fstream fp;
fp.open(path);
//一行一行读
string readLine;
while (getline(fp, readLine))
{
//存入
this->key[readLine.at(1) + 1].key = readLine.substr(4, readLine.size());
}
//关闭文件
fp.close();
}
/*********************************************************
函数名称:获取外部密文函数
函数参数:string path - 密文文件路径,若传入DEFAULT则为默认
路径
返回类型:void - 无
函数介绍:获取外部密文,此操作会替换原密文
**********************************************************/
void HuffmanTree::getSecretText(string path)
{
if (path == DEFAULT)
{
path = "secretText.txt";
}
//打开文件
fstream fp;
fp.open(path);
if (!fp.is_open())//打开失败就返回
return;
this->secretText = "";
fp >> this->secretText;
//关闭文件
fp.close();
}
/*********************************************************
函数名称:密文是否为空函数
函数参数:void - 无
返回类型:bool :空 - true 非空 - false
**********************************************************/
bool HuffmanTree::keyIsEmply()
{
if(this->key[0].ch == '0')
return true;
else
return false;
}
///qt函数//
/*********************************************************
**********************************************************/
/*********************************************************
函数名称:打印密钥函数
函数参数:QPlainTextEdit* textEdit - 接受打印的文本编辑部件
返回类型:void - 无
函数介绍:将密钥打印到指定的文本编辑部件
**********************************************************/
void HuffmanTree::printSecretKeyToTextEdit(QPlainTextEdit* textEdit)
{
//先清空原内容
textEdit->clear();
//打印
string secretKey;
QString qsecretKey;
textEdit->setStyleSheet("#textEdit{color:rgb(0,0,0)}");
for(int i = 0; i < ASCII_MAX; i++)
{
//字符未出现
if(this->key[i+1].key == "")
continue;
//格式拼接
secretKey = "'";
secretKey += this->key[i+1].ch;
secretKey += "':";
secretKey += this->key[i+1].key;
//打印
qsecretKey = QString::fromStdString(secretKey);
textEdit->appendPlainText(qsecretKey);
}
}
/*********************************************************
函数名称:打印密文函数
函数参数:QPlainTextEdit* textEdit - 接受打印的文本编辑部件
返回类型:void - 无
函数介绍:将密文打印到指定的文本编辑部件
**********************************************************/
void HuffmanTree::printSecretTextToTextEdit(QPlainTextEdit* textEdit)
{
//先清空原内容
textEdit->clear();
//打印
string secretText = this->secretText;
QString qsecretText = QString::fromStdString(secretText);
textEdit->setStyleSheet("#textEdit{color:rgb(0,0,0)}");
textEdit->appendPlainText(qsecretText);
}
/*********************************************************
函数名称:打印明文函数
函数参数:QPlainTextEdit* textEdit - 接受打印的文本编辑部件
string status - 转换状态串
|"Success" - 转换成功
|"ERROR : Incorrect ciphertext or key" - 错误的密文或密匙
|"ERROR : The key is empty" - 密码为空
返回类型:void - 无
函数介绍:将明文打印到指定的文本编辑部件
**********************************************************/
void HuffmanTree::printTextToTextEdit(QPlainTextEdit* textEdit,string status)
{
//先清空原内容
textEdit->clear();
//打印
if(status == "Success")
{
string text = this->text;
QString qtext = QString::fromStdString(text);
textEdit->setStyleSheet("#textEdit{color:rgb(0,0,0)}");
textEdit->appendPlainText(qtext);
}
else
{
string text = status;
QString qtext = QString::fromStdString(text);
textEdit->setStyleSheet("#textEdit{color:rgb(200,50,50)}");
textEdit->appendPlainText(qtext);
}
}
有了这两个文件,只需要稍加修改(如编写私有变量的访问器)就可以创建一个控制台应用了,下面是这个哈夫曼树的使用介绍:
在使用之前,应当创建一个HuffmanTree对象,之后根据模式进行函数调用
一、明文 -> 密文、密匙
1.调用 setText(string text) 方法将明文放在哈夫曼树中;
2.调用 treeNodeListConstruct() 方法将文本数据以树节点类型存入,以方便哈夫曼树的构建;
3.调用 treeCreat() 方法创建哈夫曼树;
4.调用 creatSecretKey() 方法创建密匙,并将密匙存入密匙数组中;
5.调用 creatSecretText() 方法创建密文,并存入密文串中;
6.调用 writeSecretKeyToFile(string path) 方法以文件方式储存密钥,path为文件路径;
7.调用 writeSecretTextToFile(string path) 方法以文件方式储存密文,path为文件路径。
二、密文、密匙 -> 明文
1.调用 getSecretKey(string path) 方法读取外部密匙,path为文件路径;
2.调用 getSecretText(string path) 方法读取外部密文,path为文件路径;
3.调用 huffmanDecode() 方法将转化的明文储存在 text 私有属性中,该方法有返回值,返回的为状态串。
Qt是一个跨平台的C++ 开发库,主要用来开发图形用户界面程序。在此Qt安装我就不多加赘述,读者可搜其他博主的文章学习。
Qt基本使用很多博主都有教程,在此我就不多加赘述,读者可自行搜索学习。由于本程序较为简单,并未使用到较复杂的信号与槽,读者可放心食用。
本程序所使用的部件:
1.Label : 用于文本框的说明
2.PlainText Edit : 用于文本输出
3.Push Button : 用于方法调用
读者可着重注意以上部件的使用。
Qt中创建一个项目,将 huffmanTree.cpp 和 huffmanTree.h 加入到项目中,至此就可以进行图形化编程了.
下面是我的窗口ui,读者可以此为参考
原文中可默认输入方法为:
右击原文文本编辑部件 -> 改变普通文本 -> 输入默认文字 -> 确定
密文与密匙可通过设置改为 只读 状态:
按钮也可以通过设置进行鼠标悬停以显示提示信息:
右击按钮部件 -> 改变工具提示 -> 输入工具提示
在按钮右击后使用“转到槽”,选中“clicked()”即可进行函数编写。由于要使用 HuffmanTree 对象,需要在 mainwindow.h 中包含 huffmanTree.h ,在 MainWindow 类中( MainWindow 类在mainWindow.h 中)添加 HuffmanTree 的对象。以下是该 mainwindow.cpp 的代码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
//原文转换按钮
void MainWindow::on_startTurnPButtom_clicked()
{
//明文 -> 密文、密匙
if(mainTree.keyIsEmply() == true)
{
QString qtext = ui->textEdit->toPlainText(); //接受明文窗口的字符串
string text = qtext.toStdString();
mainTree.setText(text);//输入密文
mainTree.treeNodeListConstruct();//构建树节点
mainTree.treeCreat();//创建哈夫曼树
mainTree.creatSecretKey();//创建密钥
mainTree.creatSecretText();//创建密文
//打印密文
mainTree.printSecretTextToTextEdit(ui->secretTextEdit);
//打印密匙
mainTree.printSecretKeyToTextEdit(ui->secretKeyEdit);
return;
}
}
//保存密文密钥按钮
void MainWindow::on_saveTextPButton_clicked()
{
mainTree.writeSecretKeyToFile(DEFAULT);//以默认名称创建密匙文件
mainTree.writeSecretTextToFile(DEFAULT);//以默认名称创建密文文件
}
//清除按钮
void MainWindow::on_clearPButton_clicked()
{
//清空文本框
ui->textEdit->clear();
ui->secretTextEdit->clear();
ui->secretKeyEdit->clear();
//清除数据
mainTree.huffmanTreeInit();
}
//获取外部密文按钮
void MainWindow::on_getSecretTextPButton_clicked()
{
//获取外部密文
mainTree.getSecretText(DEFAULT);
//重新打印密文
mainTree.printSecretTextToTextEdit(ui->secretTextEdit);
}
//获取外部密钥按钮
void MainWindow::on_getsecretKeyPButton_clicked()
{
//获取外部密钥
mainTree.getSecretKey(DEFAULT);
//重新打印密钥
mainTree.printSecretKeyToTextEdit(ui->secretKeyEdit);
}
//密文密匙转换按钮
void MainWindow::on_toTextPButton_clicked()
{
string status;
status = mainTree.huffmanDecode();//解码完成
//打印明文
mainTree.printTextToTextEdit(ui->textEdit,status);
}
注:在 huffmanTree.h 中需要包含 ui_mainwindow.h ,打印函数需要 QPlainTextEdit* 类的参数。
终于到这一步了,这是该项目的最后一个步骤,读者也可以在别的博主的文章中学到。就先偷个懒了......
终于写完了,呼~。那就浅浅的求个赞吧。欢迎读者前来交流哦。