一、实验题目及要求
题目:英汉翻译小词典
需求:
1、根据所提供的词典http://github.com/1eez/103976建立英文词典;
2、任意输入单词,判断该单词是否在词典中,输出查找结果,同时输入单词匹配过程中对比的中间关键字;
3、模仿Google的搜索界面,根据用户输入实时显示备选词。
二、概要设计
本次实验所用的数据结构为二叉搜索树BST,BST树具有以下特点:左子树上所有结点的值均小于或等于它的根结点的值;右子树上所有结点的值均大于或等于它的根结点的值;左、右子树也分别为二叉排序树。如果使用BST树将数据组织起来,树的每个结点都包含了健值 key、数据值 data、左子女指针、右子女指针。其中键值 key 是最核心的部分,它的值决定了树的组织形状;左子节点指针指向左子节点;右子节点指针指向右子节点;数据值 data 是该结点对应的数据,可以根据键值key找到数据data,因此适用于词典中单词的存储。
设计了两种建树的方法,分别为常规方法和折半方法;对BST树的操作有插入结点和搜索结点。使用Qt进行窗口交互设计,在widget.h中,widget实现的功能有读取csv文件、顺序方法搜索查找和BST搜索查找;用两个QStringList来分别存储英语单词和释义,方便QCompleter的使用,接收顺序输入序列时,采用折半思想,先问左侧数据,再访问右侧数据,以此递归。
由于英文词典中存在汉字字符,所以必须解决中文乱码问题。在使用 QTextStream 读写有中文内容的文本文件时,为了能正确识别 Unicode 码,需要调用 setAutoDetectUnicode(true),设置 QTextStream 可以自动识别 Unicode 码,如果不做此设置,读取文件的中文将是乱码,无法正常显示。为解决 Unicode 的识别问题,可以在应用程序中做全局的设置,使得应用程序支持 Unicode,方法是在 main() 函数中使用 QTextCodec 类进行编码设置。在本次实验中,在main()函数中添加语句
QTextCodec *codec=QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);
这样就设置了应用程序使用的编码解码器,在应用程序内就有了对 Unicode 码的支持。在前面的 openTextByStream() 函数中,即使不用 setAutoDetectUnicode(true) 也可以正常显示汉字了。
三、详细设计
在BST结点类中定义了QString类型的data和BSTNode*类型的left和right,分别为左孩子指针和右孩子指针;在BST树类中定义了BSTNode类型的根节点root指针,并有Insert、Build1、Build2三个函数成员,分别执行插入节点、常规方法建树和折半方法建树操作。
struct BSTNode
{
QString data;
BSTNode *left,*right;
//构造函数
BSTNode():left(NULL),right(NULL){}
BSTNode(QString str,BSTNode *l=NULL,BSTNode *r=NULL):data(str),left(l),right(r){}
//析构函数
~BSTNode(){}
};
class BST
{
public:
BSTNode *root;
//构造和析构
BST():root(NULL){}
BST(BSTNode *r):root(r){}
~BST(){}
bool Insert(QString str,BSTNode *&rootPtr);//插入结点
bool Build1(QVector<QString> v);//常规方法建树
bool Build2(QVector<QString> v);//折半方法建树
BSTNode* Search(QString str,BSTNode *rootPtr,int &amount);//搜索结点
};
插入结点操作,如果rootPtr指针为空,就用new关键字动态分配内存空间;否则如果str小于rootPtr的data,就递归地调用Insert函数插入左子女,如果str大于rootPtr的data,就递归地调用Insert函数插入右子女。
bool BST::Insert(QString str,BSTNode *&rootPtr)
{
if(rootPtr==NULL)
{
rootPtr=new BSTNode(str);
if(rootPtr==NULL)
qDebug()<<"错误!";
return true;
}
else if(str<rootPtr->data)
Insert(str,rootPtr->left);
else if(str>rootPtr->data)
Insert(str,rootPtr->right);
else
return false;//结点已存在
}
常规方法建立BST树,如果动态数组v的大小为0,返回false;否则采用for循环调用Insert函数依次插入每个结点。
bool BST::Build1(QVector<QString> v)
{
if(v.size()==0)
return false;
for(int i=0;i<v.size();i++)
Insert(v.at(i),root);
return true;
}
折半方法建立BST树,设置变量mid值为v的大小的一半,分别在左侧和右侧序列进行插入。
bool BST::Build2(QVector<QString> v)
{
if (v.size() == 0)
return false;
int mid;
mid = v.size() / 2;
Insert(v[mid], root);
QVector<QString> pre = v;
for (int i = 0; i < v.size() - mid; i++)
{
QVector<QString>::iterator pEnd = pre.end();
pre.erase(--pEnd);//折半左侧序列
}
Build2(pre);
QVector<QString> post = v;
for (int i = 0; i < mid + 1; i++)
{
QVector<QString>::iterator pBegin = post.begin();
post.erase(pBegin);//折半右侧序列
}
Build2(post);
}
搜索结点操作,设置BSTNode*类型工作指针temp,初始值赋为rootPtr,在while循环中,用int类型的pos指示位置信息;在for循环中,如果出现temp->data.at(i)==' '就把i的值赋给pos,再定义字符串s,赋值为temp->data.mid(0, pos),将str与s进行比较,如果str较小,temp就指向左孩子,如果str较大,temp就指向右孩子,如果相等就返回temp,从而完成搜索结点操作。
BSTNode* BST::Search(QString str,BSTNode* rootPtr,int &amount)
{
BSTNode* temp = new BSTNode;
temp = rootPtr;
while (temp != NULL)
{
int pos=0;
for(int i=0;i<temp->data.size();i++)
{
if(temp->data.at(i)==' ')
{
pos=i;
break;
}
}
QString s = temp->data.mid(0, pos);
qDebug()<<s;
amount++;
if (str < s)
temp = temp->left;
else if (str > s)
temp = temp->right;
else
return temp;
}
return NULL;
}
ui界面已设置显示文本框read_only,按钮及文本框字体已设置;用两个QStringList来分别存储英语单词和释义,方便QCompleter的使用;在接收顺序输入序列时,先输入最中间数据,再访问左侧数据,再访问右侧数据,并以此递归。在widget.cpp中,先读取csv文件,之后实现搜索框下拉选项显示,之后把单词表和翻译表合并,方便BST搜索。
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setFixedSize(420,680);
ui->lineEdit->setPlaceholderText("Please enter:");//文本框提示
//csv文件读取
QStringList *word=new QStringList;
QStringList *trans=new QStringList;
ReadCsvFile(word,trans);
//搜索框下拉选项实现
QCompleter *MyInfor = new QCompleter(*word);
MyInfor->setMaxVisibleItems(10);//设置下拉选项可见数量
ui->lineEdit->setCompleter(MyInfor);
QStringList *data=new QStringList;
if(word->size()==trans->size())
{
for(int i=0;i<word->size();i++)
{
QString str;
str=word->at(i)+' '+trans->at(i);
data->push_back(str);
}
}
读取csv文件操作,QFile 类是Qt中进行文件读写操作的类,使用 QFile 可以直接打开或保存文本文件。QFile::open() 函数打开文件时需要传递 QIODevice::OpenModeFlag 枚举类型的参数,决定文件以什么方式打开,QIODevice::ReadOnly为以只读方式打开文件,用于载入文件。
void Widget::ReadCsvFile(QStringList *word,QStringList *trans)
{
QFile inFile(":/data/EnWords.csv");
QStringList lines;
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream stream_text(&inFile);
while (!stream_text.atEnd())
{
lines.push_back(stream_text.readLine());
}
for (int j = 0; j < lines.size(); j++)
{
QString line = lines.at(j);
int pos=0;
for(int i=0;i<line.size();i++)
{
if(line[i]==',')
{
pos=i;
break;
}
}
QString str1=line.mid(0,pos);
QString str2=line.mid(pos+1,line.size()-pos-1);
//去除引号
str1.remove(0,1);
str1.remove(str1.size()-1,1);
word->push_back(str1);
str2.remove(0,1);
str2.remove(str2.size()-1,1);
trans->push_back(str2);
}
inFile.close();
}
}
顺序表查词操作,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足v[i].at(j)==' ',则查找成功,返回该元素在线性表中的位置。
QString Widget::SearchTrans1(QVector<QString>v,QString str)//顺序表搜索查词
{
int amount=0;
for(int i=0;i<v.size();i++)
{
int pos=0;
for(int j=0;j<v[i].size();j++)
{
if(v[i].at(j)==' ')
{
pos=j;
break;
}
}
QString s = v[i].mid(0, pos);//分割字符串
if(str==s)
{
amount=i+1;
qDebug()<<"对比次数"<<amount;
return v[i];
}
}
return QString("0");
}
使用BST树查词操作,用bst对象调用Search函数,并设置了变量amount记录对比次数。
QString Widget::SearchTrans2(BST bst,QString str)//BST搜索查词
{
BSTNode *bn=new BSTNode;
int amount=0;
bn=bst.Search(str,bst.root,amount);
if(bn==NULL)
return QString("0");
else
{
qDebug()<<"对比次数为:"<<amount;
return bn->data;
}
}
四、测试结果
界面显示:
下拉词汇显示:
查询结果:
五、源代码
bst.cpp
#include "bst.h"
#include <QDebug>
bool BST::Insert(QString str,BSTNode *&rootPtr)//插入结点
{
if(rootPtr==NULL)
{
rootPtr=new BSTNode(str);
if(rootPtr==NULL)
qDebug()<<"错误!";
return true;
}
else if(str<rootPtr->data)
Insert(str,rootPtr->left);
else if(str>rootPtr->data)
Insert(str,rootPtr->right);
else
return false;
}
bool BST::Build1(QVector<QString> v)//常规方法建树
{
if(v.size()==0)
return false;
for(int i=0;i<v.size();i++)
Insert(v.at(i),root);
return true;
}
bool BST::Build2(QVector<QString> v)//折半建树
{
if (v.size() == 0)
return false;
int mid;
mid = v.size() / 2;
Insert(v[mid], root);
QVector<QString> pre = v;
for (int i = 0; i < v.size() - mid; i++)
{
QVector<QString>::iterator pEnd = pre.end();
pre.erase(--pEnd);//折半左侧序列
}
Build2(pre);
QVector<QString> post = v;
for (int i = 0; i < mid + 1; i++)
{
QVector<QString>::iterator pBegin = post.begin();
post.erase(pBegin);//折半右侧序列
}
Build2(post);
}
BSTNode* BST::Search(QString str,BSTNode* rootPtr,int &amount)//搜索结点
{
BSTNode* temp = new BSTNode;
temp = rootPtr;
while (temp != NULL)
{
int pos=0;
for(int i=0;i<temp->data.size();i++)
{
if(temp->data.at(i)==' ')
{
pos=i;
break;
}
}
QString s = temp->data.mid(0, pos);
qDebug()<<s;
amount++;
if (str < s)
temp = temp->left;
else if (str > s)
temp = temp->right;
else
return temp;
}
return NULL;
}
bst.h
#ifndef BST_H
#define BST_H
#include <stdlib.h>
#include <QString>
#include <QVector>
struct BSTNode
{
QString data;
BSTNode *left,*right;
//构造函数
BSTNode():left(NULL),right(NULL){}
BSTNode(QString str,BSTNode *l=NULL,BSTNode *r=NULL):data(str),left(l),right(r){}
//析构函数
~BSTNode(){}
};
class BST
{
public:
BSTNode *root;
//构造和析构
BST():root(NULL){}
BST(BSTNode *r):root(r){}
~BST(){}
bool Insert(QString str,BSTNode *&rootPtr);//插入结点
bool Build1(QVector<QString> v);//常规方法建树
bool Build2(QVector<QString> v);//折半方法建树
BSTNode* Search(QString str,BSTNode *rootPtr,int &amount);//搜索结点
};
#endif // BST_H
main.cpp
#include "widget.h"
#include <QApplication>
#include <QTextCodec>
int main(int argc, char *argv[])
{
QTextCodec *codec=QTextCodec::codecForName("UTF-8");//使用UTF8使中文正常显示
QTextCodec::setCodecForLocale(codec);
QApplication a(argc, argv);
Widget w;
w.setWindowTitle("Translate");
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setFixedSize(420,680);
ui->lineEdit->setPlaceholderText("Please enter:");
//csv文件读取
QStringList *word=new QStringList;
QStringList *trans=new QStringList;
ReadCsvFile(word,trans);
QCompleter *MyInfor = new QCompleter(*word);
MyInfor->setMaxVisibleItems(10);//设置下拉选项可见数量
ui->lineEdit->setCompleter(MyInfor);
QStringList *data=new QStringList;
if(word->size()==trans->size())
{
for(int i=0;i<word->size();i++)
{
QString str;
str=word->at(i)+' '+trans->at(i);
data->push_back(str);
}
}
QVector<QString> v;
for(int i=0;i<data->size();i++)
v.push_back(data->at(i));
BST bst;
//bst.Build1(v);//常规BST建树
bst.Build2(v);//折半建树
connect(ui->pushButton,&QPushButton::clicked,[=](){
QString desWord=ui->lineEdit->text();
//BST查找方式
//QString desTrans=SearchTrans2(bst,desWord);
//顺序查找方式
QString desTrans=SearchTrans1(v,desWord);
if(desTrans=="0")
ui->textEdit->setText("Not Found:(");
else
ui->textEdit->setText(desTrans);
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::ReadCsvFile(QStringList *word,QStringList *trans)//读取csv文件
{
QFile inFile(":/data/EnWords.csv");
QStringList lines;
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream stream_text(&inFile);
while (!stream_text.atEnd())
{
lines.push_back(stream_text.readLine());
}
for (int j = 0; j < lines.size(); j++)
{
QString line = lines.at(j);//逐行取出
//以逗号分割当前行
int pos=0;//第一个逗号的位置
for(int i=0;i<line.size();i++)
{
if(line[i]==',')
{
pos=i;
break;
}
}
QString str1=line.mid(0,pos);
QString str2=line.mid(pos+1,line.size()-pos-1);
//去除引号
str1.remove(0,1);
str1.remove(str1.size()-1,1);
word->push_back(str1);
str2.remove(0,1);
str2.remove(str2.size()-1,1);
trans->push_back(str2);
}
inFile.close();
}
}
QString Widget::SearchTrans1(QVector<QString>v,QString str)//顺序表搜索查词
{
int amount=0;
for(int i=0;i<v.size();i++)
{
int pos=0;
for(int j=0;j<v[i].size();j++)
{
if(v[i].at(j)==' ')
{
pos=j;
break;
}
}
QString s = v[i].mid(0, pos);//分割字符串
if(str==s)
{
amount=i+1;
qDebug()<<"对比次数"<<amount;
return v[i];
}
}
return QString("0");
}
QString Widget::SearchTrans2(BST bst,QString str)//BST搜索查词
{
BSTNode *bn=new BSTNode;
int amount=0;
bn=bst.Search(str,bst.root,amount);
if(bn==NULL)
return QString("0");
else
{
qDebug()<<"对比次数为:"<<amount;
return bn->data;
}
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QStringList>
#include <QString>
#include <QCompleter>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QVector>
#include "bst.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void ReadCsvFile(QStringList *word,QStringList *trans);//读取csv文件
QString SearchTrans1(QVector<QString>v,QString str);//顺序表搜索查词
QString SearchTrans2(BST bst,QString str);//BST搜索查词
private:
Ui::Widget *ui;
};
#endif