【数据结构】基于哈希表的成语查询系统

概要

项目要求

汉语词组按字位查询的数据结构与算法设计
【问题描述】
汉语写作(作文)时,作者常遇到这样的需求:只模糊的记得一个汉字和它的位置,这个四字词组却记不得了。请你设计一个工具软件帮助作者。
【基本要求】
(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* &currentNode = 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* &currentNode = 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文件读取乱码

在这里插入图片描述

小结

仅仅简单实现了哈希表,对哈希表的了解有待深入。
对于汉字如何哈希化这个问题还有疑惑。暂且存疑,待后续探究。
因作者水平有限,如有错误,敬请指正。
感谢向我提供帮助的各位同志!

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值