哈夫曼树,但图形化(数据结构课设cpp)

目录

一、 概述

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.1 前言

大一的课程已经快结束了,临近期末,数据结构与算法的课设也开始了,我的课设题目是哈夫曼树,最基本的要求是能够构建哈夫曼树并输出密文与密匙,当然既然决定要好好完成就不能这么敷衍,下面我来分享我的课设。

1.2 功能

1.要有图形化

          哈夫曼树,但图形化(数据结构课设cpp)_第1张图片

2.可以进行文件输出

               

3.可以通过文件读取密文、密匙进行解译

          哈夫曼树,但图形化(数据结构课设cpp)_第2张图片

二、代码实现

2.1 哈夫曼树类

huffmanTree.h

        这里面的 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

huffmanTree.cpp

里面有详细注释,所以读者阅读应该也很方便

#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);

    }

}

 2.2 使用介绍

        有了这两个文件,只需要稍加修改(如编写私有变量的访问器)就可以创建一个控制台应用了,下面是这个哈夫曼树的使用介绍:

        在使用之前,应当创建一个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进行窗口化

3.1 Qt 安装

        Qt是一个跨平台的C++ 开发库,主要用来开发图形用户界面程序。在此Qt安装我就不多加赘述,读者可搜其他博主的文章学习。

3.2 Qt 基本使用

        Qt基本使用很多博主都有教程,在此我就不多加赘述,读者可自行搜索学习。由于本程序较为简单,并未使用到较复杂的信号与槽,读者可放心食用。

        本程序所使用的部件:

        1.Label : 用于文本框的说明

        2.PlainText Edit : 用于文本输出

        3.Push Button : 用于方法调用

        读者可着重注意以上部件的使用。

3.3 Qt编程

        Qt中创建一个项目,将 huffmanTree.cpphuffmanTree.h 加入到项目中,至此就可以进行图形化编程了.

 3.3.1 窗口设置

        下面是我的窗口ui,读者可以此为参考

        哈夫曼树,但图形化(数据结构课设cpp)_第3张图片

         原文中可默认输入方法为:

        右击原文文本编辑部件 -> 改变普通文本 -> 输入默认文字 -> 确定

        密文与密匙可通过设置改为 只读 状态:

        哈夫曼树,但图形化(数据结构课设cpp)_第4张图片

        按钮也可以通过设置进行鼠标悬停以显示提示信息:

        右击按钮部件 -> 改变工具提示 -> 输入工具提示 

3.3.2 按钮按下函数

        在按钮右击后使用“转到槽”,选中“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* 类的参数。

3.4 项目打包

        终于到这一步了,这是该项目的最后一个步骤,读者也可以在别的博主的文章中学到。就先偷个懒了......

四、结尾

终于写完了,呼~。那就浅浅的求个赞吧。欢迎读者前来交流哦。

你可能感兴趣的:(数据结构,c++)