概要
项目要求
汉语词组按字位查询的数据结构与算法设计
【问题描述】
汉语写作(作文)时,作者常遇到这样的需求:只模糊的记得一个汉字和它的位置,这个四字词组却记不得了。请你设计一个工具软件帮助作者。
【基本要求】
(1)输入一个汉字和位置。
(2)输出形式:输出词组库中所有符合条件的四字词组。
【测试数据】
输入 1:天 1
输出 1:天上人间 天高地厚 天翻地覆
输入 2:天 2
输出 2:偷天换日 顶天立地 开天辟地
输入 3:天 3
输出 3:国色天香 地老天荒
输入 4:天 4
输出 4:叫苦连天 别有洞天
【实现提示】
首先创建并存储一个汉语词组库(词组数量大于 200 条)的数据结构,然后设计一种搜索方法或搜索结构,最后实现查询和输出。
项目成果
汉语词组库为自定义
项目思路
1.选择哈希表为查找结构,实现T = O(1)的查找
2.选择链表为基础的存储结构
3.冲突策略:链表+头插法(降低时间复杂度)
4.成语库为本地txt文件存储
项目代码
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#define ARRAY_SIZE 9000
#define HASH_FACTOR 8999
using namespace std;
class IdiomNode {
private:
string key;
string value;
IdiomNode* link;
public:
IdiomNode() {
key = "";
value = "";
link = nullptr;
}
IdiomNode(string keyV, string valueV, IdiomNode* linkV) {
key = keyV;
value = valueV;
link = linkV;
}
string getKey();
string getValue();
IdiomNode* getLink();
void setLink(IdiomNode* linkV);
};
string IdiomNode::getKey() {
return key;
}
string IdiomNode::getValue() {
return value;
}
IdiomNode* IdiomNode::getLink() {
return link;
}
void IdiomNode::setLink(IdiomNode* linkV) {
link = linkV;
}
class HashTable {
private:
IdiomNode* addressesArray[ARRAY_SIZE] = {nullptr};
public:
void init();
int creatIndex(string keyV);
void enter(string valueV);
void insert(string keyV, string valueV);
bool find(string keyV);
};
void HashTable::init() {
ifstream idiomFile;
idiomFile.open("Idiom.txt", ios::in);
if (!idiomFile.is_open()) {
cerr << "打开文件时发生错误:" << "Idiom.txt" << endl;
exit(1);
}
string ValueV;
while (getline(idiomFile, ValueV)) {
enter(ValueV);
}
idiomFile.close();
cout << "样例输入:天1" << endl;
cout << "样例输出:" << endl;
}
int HashTable::creatIndex(string keyV) {
hash<string> stringHash;
size_t index = stringHash(keyV);
index %= HASH_FACTOR;
return static_cast<int>(index);
}
void HashTable::enter(string valueV) {
for (int i = 0; i < 8; i += 2) {
string keyV = valueV.substr(i, 2) + to_string((i / 2) + 1);
insert(keyV, valueV);
}
}
void HashTable::insert(string keyV, string valueV) {
int index = creatIndex(keyV);
IdiomNode* ¤tNode = addressesArray[index];
if (currentNode) {
IdiomNode* newNode = new IdiomNode(keyV, valueV, currentNode);
currentNode = newNode;
} else {
currentNode = new IdiomNode(keyV, valueV, nullptr);
}
}
bool HashTable::find(string keyV) {
int is_succeed = 0;
int index = creatIndex(keyV);
IdiomNode* currentNode = addressesArray[index];
int col = 0;
while (currentNode) {
if (currentNode->getKey() == keyV) {
if(++is_succeed == 1) {
cout << "输出:" << endl;
}
cout << currentNode->getValue() << " ";
if (++col == 5) {
cout << endl;
col = 0;
}
}
currentNode = currentNode->getLink();
}
if (col) {
cout << endl;
}
if (!is_succeed) {
cout << "数据库不存在此类成语!" << endl;
return false;
}
return true;
}
int main() {
HashTable ht;
ht.init();
string valueV = "天1";
while (valueV != "EXIT") {
ht.find(valueV);
cout << "请输入您要查询的汉字和位置(输入EXIT则退出):" << endl;
cin >> valueV;
system("cls");
cout << valueV << endl;
}
}
成语库见资源
整体实现
链表实现
class IdiomNode {
private:
string key; //关键字 如“天1”
string value; //值 如“天下为公”
IdiomNode* link; //下一个节点地址
public:
IdiomNode() {
key = "";
value = "";
link = nullptr;
}
IdiomNode(string keyV, string valueV, IdiomNode* linkV) {
key = keyV;
value = valueV;
link = linkV;
}
string getKey();
string getValue();
IdiomNode* getLink();
void setLink(IdiomNode* linkV);
};
string IdiomNode::getKey() {
return key;
}
string IdiomNode::getValue() {
return value;
}
IdiomNode* IdiomNode::getLink() {
return link;
}
void IdiomNode::setLink(IdiomNode* linkV) {
link = linkV;
}
哈希表实现
//哈希表实现
class HashTable {
private:
IdiomNode* addressesArray[ARRAY_SIZE] = {nullptr}; //用数组存放节点地址
public:
void init(); //初始化 导入本地成语库
int creatIndex(string keyV); //哈希函数 实现关键字映射到数组
void enter(string valueV); //录入成语 如“天下为公”
void insert(string keyV, string valueV); //创建链表节点 并插入哈希表
bool find(string keyV); //根据关键字查找成语
};
void HashTable::init() {
ifstream idiomFile;
idiomFile.open("Idiom.txt", ios::in); //以只读方式打开本地成语库
if (!idiomFile.is_open()) {
cerr << "打开文件时发生错误:" << "Idiom.txt" << endl;
exit(1);
}
string ValueV;
while (getline(idiomFile, ValueV)) { //按行录入成语 每次一个
enter(ValueV);
}
idiomFile.close();
cout << "样例输入:天1" << endl;
cout << "样例输出:" << endl;
}
int HashTable::creatIndex(string keyV) {
hash<string> stringHash; //字符串哈希化
size_t index = stringHash(keyV);
index %= HASH_FACTOR; //除留余数法
return static_cast<int>(index); //返回索引值
}
void HashTable::enter(string valueV) {
for (int i = 0; i < 8; i += 2) {
string keyV = valueV.substr(i, 2) + to_string((i / 2) + 1); //获取单个汉字与其位置
insert(keyV, valueV);
}
}
void HashTable::insert(string keyV, string valueV) {
int index = creatIndex(keyV);
IdiomNode* ¤tNode = addressesArray[index]; //引用 以便修改数组中的节点地址
if (currentNode) { //已有节点时 采用头插法
IdiomNode* newNode = new IdiomNode(keyV, valueV, currentNode);
currentNode = newNode;
} else { //无节点时 直接插入
currentNode = new IdiomNode(keyV, valueV, nullptr);
}
}
bool HashTable::find(string keyV) {
int is_succeed = 0; //判断是否查询到成语
int index = creatIndex(keyV);
IdiomNode* currentNode = addressesArray[index];
int col = 0; //控制换行 每五个成语一行
while (currentNode) {
if (currentNode->getKey() == keyV) { //一个数组元素下可能存放不同关键字的成语
if(++is_succeed == 1) { //能查询到成语时 显示提示语
cout << "输出:" << endl;
}
cout << currentNode->getValue() << " ";
if (++col == 5) {
cout << endl;
col = 0;
}
}
currentNode = currentNode->getLink();
}
if (col) {
cout << endl;
}
if (!is_succeed) { //is_succeed = 0
cout << "数据库不存在此类成语!" << endl;
return false;
}
return true;
}
技术细节
size_t与int区别
1.字节大小
size_t在32位架构上是4字节,在64位架构上是8字节
int在不同架构下都是4字节
2.符号
size_t为无符号数
int为有符号数
字符串中获取单个汉字
string idiom = “天下为公”;
cout << idiom.size(); //输出8
for (int i = 0; i < 8; i += 2) {
string character = idiom.substr(i, 2); //获取单个汉字
cout << character; //输出天
}
箭头运算符(->)和点运算符(.)区别
箭头运算符:左边操作数为指针类型(间接)
点运算符:左边操作数为实例类型(直接)
txt文件读取乱码
小结
仅仅简单实现了哈希表,对哈希表的了解有待深入。
对于汉字如何哈希化这个问题还有疑惑。暂且存疑,待后续探究。
因作者水平有限,如有错误,敬请指正。
感谢向我提供帮助的各位同志!