【数据结构实验】哈希表设计

news2024/11/18 15:41:00

【数据结构实验】哈希表设计

简介

针对本班同学中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建表和查表程序。文末贴出了源代码。

需求分析

  1. 假设人名为中国人姓名的汉语拼音形式,待填入哈希表的人名共有三十个左右,取平均查找长度上限为2,哈希函数用除留余数法构造,用伪随机探测再散列法处理冲突。
  2. 人名的长度均不超过19个字符,最长的人名如:庄双双(Zhuang Shuangshuang)。
  3. 应充分研究这些人名的特点,尽量找到一个冲突较小的哈希函数,使得存储时分布尽量均匀。

概要设计

  1. 用线性表顺序存储结构存储哈希表,哈希表及其中元素的定义如下:
typedef char* KeyType;
typedef struct ElemType
{
	KeyType key;
}ElemType;
typedef struct
{
	ElemType *elem;
	int count;
	int sizeindex;
}HashTable;
  1. 主程序:
int main()
{
			初始化;
		do{
			接受命令;
			处理命令;
     }while(“命令”!=“退出”)
}
  1. 本程序共有个4模块,调用关系如下:

模块调用关系

在建立哈希表时,就是通过反复搜索哈希表中的元素进行哈希表的建立。

详细设计

  1. 所用到的函数声明如下:
/*函数声明*/ 
Status InitHashTable(HashTable &H);
//初始化哈希表
void Get_RandomSequence();
//生成随机序列
void collision(int &p,int c);
//地址冲突时进行下一次探测
Status DestroyHashTable(HashTable &H);
//删除哈希表
int Hash(KeyType K);
//求出哈希地址
Status InsertHash(HashTable &H, ElemType e);
//将元素e插入哈希表中
void RecreateHashTable(HashTable *H);
//空间不足时,重新分配哈希表
bool equal(KeyType K1,KeyType K2);
//判断两个关键字是否相等
Status SearchHash(HashTable H, KeyType K, int &p, int &c);
//在哈希表中搜索相应元素
Status BulidHash(HashTable &H);
//建立哈希表
void PrintHash(HashTable H);
//打印哈希表
void onScreen();
//生成用户界面,供用户输入选择

2.完整代码:

/*头文件*/ 
#include<stdio.h>
#include<iostream>
#include <cstring>
#include <ctype.h>
#include<string.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>

/*宏定义*/ 
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define OK 1
#define ERROR 0
#define MaxNameSize 19
#define NULLKEY NULL

using namespace std;
typedef int Status;
typedef char* KeyType;
typedef struct ElemType
{
	KeyType key;
}ElemType;
typedef struct
{
	ElemType *elem;
	int count;
	int sizeindex;
}HashTable;
int length;//length为表长度 
int hashsize[]={71,73,79,83,89,97};
int RandomSequence[50]; 

/*函数声明*/ 
Status InitHashTable(HashTable &H);
void Get_RandomSequence();
void collision(int &p,int c);
Status DestroyHashTable(HashTable &H);
int Hash(KeyType K);
Status InsertHash(HashTable &H, ElemType e);
void RecreateHashTable(HashTable *H);
bool equal(KeyType K1,KeyType K2);
Status SearchHash(HashTable H, KeyType K, int &p, int &c);
Status BulidHash(HashTable &H);
void PrintHash(HashTable H);
void onScreen();
 
int main()
{
	HashTable H;	
	InitHashTable(H);
	Get_RandomSequence();
	onScreen();
	char choice;
	char* want;
	int p=0;int c=0;
	want=new char[MaxNameSize];
	BulidHash(H);
	while(1)
	{
		fflush(stdin);
		cin>>choice;
		switch(choice)
		{	
			case 'P':
				PrintHash(H);
				onScreen();
				break;
			case 'S':
				while(1)
				{
					c=0;
					p=0;
					cout<<"开始查找,输入quit返回"<<endl;
					fflush(stdin);
					gets(want);
					if(strcmp(want,"quit")==0)
					break;
					if(SearchHash(H,want,p,c)==SUCCESS)
					{
						cout<<"已找到"<<endl;
						cout<<"哈希地址为:"<<p<<"\t查找次数:"<<c+1<<endl; 
					}
					else
					{
						cout<<"未找到"<<endl;
						cout<<"查找次数:"<<c+1<<endl; 
					}	
					delete(want);
					want=new char[MaxNameSize];
				}
				onScreen();
				break;
			case 'Q':
				exit(0);
				break;
			default:
				cout<<"输入错误"<<endl;
				break;
		} 
	}
}

void onScreen()
{
	cout<<"-------------------------欢迎使用哈希表查找----------------------------------"<<endl;
	cout<<"请选择需要进行的操作(输入相应字母):"<<endl;
	cout<<"P.打印哈希表"<<endl;
	cout<<"S.查找元素"<<endl;
	cout<<"Q.退出程序"<<endl;
}

Status InitHashTable(HashTable &H)
{
	H.count=0;
	H.sizeindex=0;
	length=hashsize[H.sizeindex];
	H.elem=new ElemType[length];
	if(!H.elem) exit(OVERFLOW);
	for(int i=0;i<length;i++)
	H.elem[i].key=NULLKEY;
	return OK;
}

void Get_RandomSequence()
{
	srand((int)time(NULL));
	for(int i=0;i<50;i++)
	RandomSequence[i]=rand()%length;
}

void collision(int &p,int c)
{
//p = (p + c) % length;

	p=(p+RandomSequence[c])%length;
//	p=(int)(p+pow(-1,c)*pow(2,c))%(length/2);
}

Status DestroyHashTable(HashTable &H)
{
	delete(H.elem);
	H.count=0;
	H.sizeindex=0;
	return OK;
}

int Hash(KeyType K)
{
	if(length>0)
	{
		return (K[0]+K[strlen(K)-1]+K[strlen(K)-2])%length;
	}
	
	else
	return -1;
}


void RecreateHashTable(HashTable *H) 
{
	int i, count = (*H).count;
	int m=length;
	ElemType *a, *elem = (ElemType*) malloc(count * sizeof(ElemType));
	a = elem;
	//printf("重建哈希表\n");
	for (i = 0; i < m; i++) // 保存原有的数据到elem中
		if (((*H).elem + i)->key != NULLKEY) // 该单元有数据
			*elem++ = *((*H).elem + i);
	elem = a;
	(*H).count = 0;
	(*H).sizeindex++; // 增大存储容量
	m = hashsize[(*H).sizeindex];
	ElemType *p = (ElemType*) realloc((*H).elem, m * sizeof(ElemType));
	if (!p)
		exit(0); // 存储分配失败
	(*H).elem = p;
	for (i = 0; i < m; i++)
		(*H).elem[i].key = NULLKEY; // 未填记录的标志(初始化)
	for (i = 0; i < count; i++) // 将原有的数据按照新的表长插入到重建的哈希表中
		InsertHash(*H, *(elem + i));
}

bool equal(KeyType K1,KeyType K2)
{
	if(!K1||!K2) return false;
	int result=strcmp(K1,K2);
	if(result==0) 
	return true;
	else 
	return false;
}

Status SearchHash(HashTable H, KeyType K, int &p, int &c) 
{  
   // 算法9.17
   // 在开放定址哈希表H中查找关键码为K的元素,
   // 若查找成功,以p指示待查数据元素在表中位置,并返回SUCCESS;
   // 否则,以p指示插入位置,并返回UNSUCCESS,
   // c用以计冲突次数,其初值置零,供建表插入时参考
	p = Hash(K);                          // 求得哈希地址
	while ((H.elem[p].key != NULLKEY) &&  // 该位置中填有记录
	      !equal(K, (H.elem[p].key)))   // 并且关键字不相等
	    collision(p, ++c);                 // 求得下一探查地址p
	if (equal(K, (H.elem[p].key)))
	    return SUCCESS;        // 查找成功,p返回待查数据元素位置
	else return UNSUCCESS;    // 查找不成功(H.elem[p].key == NULLKEY),
                             // p返回的是插入位置
} // SearchHash

Status InsertHash(HashTable &H, ElemType e) 
{  // 算法9.18
   // 查找不成功时插入数据元素e到开放定址哈希表H中,并返回OK;
   // 若冲突次数过大,则重建哈希表
	int c = 0;
	int p = 0;
	if (SearchHash(H, e.key, p, c) == SUCCESS )
	    return DUPLICATE;        // 表中已有与e有相同关键字的元素
	else if (c <hashsize[H.sizeindex]/2) 
	{   // 冲突次数c未达到上限,(阀值c可调)
		H.elem[p].key=e.key;
		++H.count;  
		return SUCCESS;  // 插入e
	} 
	else 
	{
		RecreateHashTable(&H);  // 重建哈希表
		return UNSUCCESS;
	} 
} // InsertHash

void PrintHash(HashTable H)
{
	cout<<"打印hash表"<<endl;
	cout<<"count\tp\tkey"<<endl<<endl;
	int count=0;
	for(int i=0;i<length;i++)
	if(H.elem[i].key!=NULLKEY)
	{
		count++;
		printf("%d\t%d\t%s\n\n",count,i,H.elem[i].key);
	//	cout<<<<"\t"<<<<endl;//H.elem[0].name
	}
	int p=0,c=0;
	float total=0;
	//char want[50];
	while(1)     //伪随机数还是有随机性的,此处可以保证平均查找长度小于2 
	{
		for(int i=0;i<length;i++)
		{
			if(H.elem[i].key!=NULLKEY) 
			{
				SearchHash(H,H.elem[i].key,p,c);
				total+=c+1;
				c=0;
			}	
		}
		if(total/33>=2) BulidHash(H);
		else break;
	}
	cout<<"平均查找长度:"<<total/33<<endl;
}


Status BulidHash(HashTable &H)
{
	FILE *fp;
	ElemType e;
	if((fp=fopen("姓名.txt","r"))==NULL)
	{
		cout<<"不能打开文件姓名.txt"<<endl;
		return ERROR;
	}
	char temp[MaxNameSize];
	while(fgets(temp,MaxNameSize,fp)!=NULL)
	{
		e.key=(char*)malloc(strlen(temp)*sizeof(char));
		strcpy(e.key,temp);
		e.key[strlen(e.key)-1]='\0';
	//	cout<<e.name<<endl;
		InsertHash(H,e);
	} 
	fclose(fp);
	return OK;
}

调试分析

  1. 在最初设计哈希函数时,采用每个人名第一个字的第一个拼音的asc码作为哈希地址,出现了较多地址冲突,具体原因是班级内有较多的同姓的同学,因此进行了适当的修改,更改为每个人名字首个字符与倒数两个字符的asc码的和对表长取余的值。
  2. 最初只有班级同学的中文姓名格式,可以通过excel获取班级同学名字的拼音格式,高效迅速。
  3. 最初在搜索时时常出现找不到某个同学的情况,具体原因是因为文件读入格式出问问题,导致名字读入不完整或者建立哈希表时未插入到正确位置。

用户手册

进入程序后,用户将看到一下界面:

---欢迎使用哈希表查找-----
请选择需要进行的操作(输入相应字母):
P.打印哈希表
S.查找元素
Q.退出程序

用户可根据需求进行选择,班级同学的姓名无需手动输入,已自动从指定文件夹中读入。

调试数据

  1. 选择P,将打印哈希表,并自动计算平均查找长度:
打印hash表
count   p       key

1       3       Jin Weiqiang

2       5       Li Qiupeng

3       9       Chen Jiayi

4       11      Wangtao

5       12      Xiong Peiyao

6       13      Zhang Huan

7       14      Sun Xiaoni

8       16      Wu Guanpeng

9       17      Wang Zhihua

10      18      Li Jingyi

11      19      Xiong Junlin

12      20      Zhang Enhua

13      21      Chen Nianyu

14      24      Yu Liang

15      25      Zhou Xuanyu

16      30      Li Wenjie

17      31      Lin Haichuan

18      35      Jing Yaxing

19      37      Li Yapeng

20      43      Xiao Yi

21      44      Lin Zhaoqian

22      45      Zhang Xiaoyuan

23      48      Wu Yiting

24      50      Wu Jiajia

25      51      Zeng Yang

26      53      Chen Xiaoyu

27      57      Yu Zihuan

28      58      Zhang Yitian

29      63      Yang Dongsheng

30      66      Zhu Jinxin

31      67      Jiang Weiwei

32      69      Jia Shihao

33      70      Fu Yongning

平均查找长度:1.87879
  1. 输入S,再输入查找人名,将自动进行搜索并返回结果。
  2. 所用到的其他文件
    姓名.txt
Chen Jiayi
Chen Nianyu
Chen Xiaoyu
Fu Yongning
Jia Shihao
Jiang Weiwei
Jin Weiqiang
Jing Yaxing
Li Jingyi
Li Qiupeng
Li Wenjie
Li Yapeng
Lin Haichuan
Lin Zhaoqian
Sun Xiaoni
Wangtao
Wang Zhihua
Wu Guanpeng
Wu Yiting
Wu Jiajia
Xiao Yi
Xiong Junlin
Xiong Peiyao
Yang Dongsheng
Yu Zihuan
Yu Liang
Zhang Enhua
Zhang Huan
Zhang Xiaoyuan
Zhang Yitian
Zeng Yang
Zhou Xuanyu
Zhu Jinxin 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/445058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汇编指令角度掌握函数调用堆栈详细过程

函数参数从右向左依次压栈push&#xff0c;call 要先把下一行指令 压栈&#xff0c; 根据如下代码思考两个问题&#xff1a; 问题一&#xff1a;main函数调用sum&#xff0c;sum执行完以后&#xff0c;怎么知道回到哪个函数中&#xff1f; 问题二&#xff1a;sum函数执行完&…

使用树状图可视化聚类

一般情况下&#xff0c;我们都是使用散点图进行聚类可视化&#xff0c;但是某些的聚类算法可视化时散点图并不理想&#xff0c;所以在这篇文章中&#xff0c;我们介绍如何使用树状图&#xff08;Dendrograms&#xff09;对我们的聚类结果进行可视化。 树状图 树状图是显示对象…

vue 项目中 向数组 添加元素 的方式

学习目标&#xff1a; 提示&#xff1a;了解 vue 向数组 添加元素 的三种方式 学习内容&#xff1a; 提示&#xff1a; vue 向数组 添加元素 的三种方式 方法&#xff1a; push()unshift()splice() 总结&#xff1a; 提示&#xff1a;这里总结相关的知识 1、push() 含义&a…

网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

文章目录 1. 什么是网络编程2. 网络编程中的基本概念1&#xff09;发送端和接收端2&#xff09;请求和响应3&#xff09;客户端和服务端4&#xff09;常见的客户端服务端模型 3. Socket 套接字1&#xff09;Socket 的分类2&#xff09;Java 数据报套接字通信模型3&#xff09;J…

【Redis7】Redis7 集群(重点:哈希槽分区)

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Redis7 集群概述、作用、集群算法-分片-槽位slot、集群环境案例步骤、集群常用操作命令和CRC16算法。 后续会继续分享Redis7和其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&am…

ChatGPT 的数据保护盲点以及安全团队如何解决这些盲点

自成立以来的短时间内&#xff0c;ChatGPT 和其他生成式 AI 平台理所当然地赢得了终极生产力助推器的声誉。 然而&#xff0c;能够按需快速生成高质量文本的技术&#xff0c;可能同时暴露敏感的公司数据。 最近发生的一起事件&#xff0c;三星软件工程师将专有代码粘贴到 ChatG…

SpringBoot集成MyBatis-yml方式详解

SpringBoot集成MyBatis-yml方式详解 简介&#xff1a;spring boot整合mybatis开发web系统目前来说是市面上主流的框架&#xff0c;每个Java程序和springboot mybatis相处的时间可谓是比和自己女朋友相处的时间都多&#xff0c;但是springboot mybatis并没有得到你的真爱&#x…

涨点技巧: 谷歌强势推出优化器Lion,引入到Yolov8,内存更小、效率更高,秒杀Adam(W)

1.Lion优化器介绍 论文:https://arxiv.org/abs/2302.06675 代码:automl/lion at master google/automl GitHub 1.1 简单、内存高效、运行速度更快 1)与 AdamW 和各种自适应优化器需要同时保存一阶和二阶矩相比,Lion 只需要动量,将额外的内存占用减半; 2)由于 Lion…

企业批量寄件快递教程

了解企业批量寄快递操作流程之前&#xff0c;我们先来解答一下这个问题&#xff1a;什么样的企业需要批量寄快递&#xff1f; 电商行业就不提&#xff0c;本身二者就是相互依存的关系。就商务件来说&#xff0c;具体什么样的企业需要批量寄快递&#xff0c;其实很简单&#xf…

Ansys Zemax | 如何使用坐标返回功能

概述 这篇文章简单介绍了如何使用OpticStudio中的坐标返回(Coordinate Return)功能。坐标返回功能可以非常方便的使系统坐标自动返回到目标表面处。&#xff08;联系我们获取文章附件&#xff09; 介绍 在OpticStudio的序列模式中&#xff0c;我们经常会使用坐标间断(Coordinat…

Netty 源码解析(上)

序 Netty的影响力以及使用场景就不用多说了&#xff0c; 去年10月份后&#xff0c;就着手研究Netty源码&#xff0c;之前研究过Spring源码&#xff0c;MyBatis源码&#xff0c;java.util.concurrent源码&#xff0c;tomcat源码&#xff0c;发现一个特点&#xff0c;之前的源码都…

PHPStudy安装imagick扩展

phpstudy软件管理中没有自带安装imagick扩展&#xff0c;只能自己安装了。 下面将用几个步骤来进行phpstudy imagick安装&#xff1a; 1.下载imagick 下载地址 ImageMagick – Download 下载电脑版本相对的64/32位&#xff0c;最新的版本 2.安装imagick 双击刚刚下载的文件…

三维可视化如何助力智慧城市建设?

在智慧城市建设中&#xff0c;如何将城市各类数据可视化是一大难题&#xff0c;目前市面上可视化的方法很多&#xff0c;如传统的三维建模、地物模型、建筑模型等。 这些方法各有利弊&#xff0c;从其实现方式来看&#xff1a; GIS/BIM是将所有的空间信息全部整合到一起; 从技术…

第三章 法的渊源与法的分类

目录 第一节 法的渊源的分类 一、法的渊源释义二、法的渊源种类 第二节 正式法源 一、正式法源的含义二、当代中国的正式法源三、正式法源的一般效力原则 第三节 非正式法源 一、当代中国的非正式法源 第四节 法的分类 一、法的一般分类二、法的特殊分类 第一节 法的渊源的…

SSH连接本地centerOS系统配置

SSH连接本地linux系统 前提&#xff1a;安装好centerOS系统并能启动 目标&#xff1a;连通外网并设置SSH 1.网卡配置文件路径 打开linux本地终端 快捷键一般是ctrlaltf2(f1~f3) 这里是管理员登录,密码是隐藏式的输入(输入看不见) 这样就显示成功登录了&#xff01; 下面查…

Unity InputSystem (二)

InputActionAssets 是包含输入操作以及其关联的绑定和控制方案的资源&#xff0c;文件以 .inputactions 文件扩展名存储&#xff0c;并且是以纯 json 文件存储的。 创建 InputActionAssets 文件 在 Assets 窗口内选择创建 InputActions 文件 ControlSchemes 控制的解决方案…

上市公司杰创智能携手甄云,启动供应链采购数字化升级

近日&#xff0c;A股上市企业杰创智能科技股份有限公司&#xff08;以下简称“杰创智能”&#xff09;联合甄云科技举办数字化采购管理项目启动会&#xff0c;双方企业高层、相关部门负责人及项目团队成员参加了此次活动。 会上&#xff0c;就杰创智能的数字化采购管理系统建设…

Flink Table API 和 Flink-SQL使用详解

Flink Table API 和 Flink-SQL使用详解 1.Table API & Flink SQL-核心概念 ​ Apache Flink 有两种关系型 API 来做流批统一处理&#xff1a; Table API Table API 是用于 Scala 和 Java 语言的查询API&#xff0c;它可以用一种非常直观的方式来组合使用选取、过滤、join…

2023-04-21 学习记录--C/C++-实现升序降序(冒泡法/沉底法)

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、冒泡法(沉底法) —— 升序 ⭐️ &#xff08;一&#xff09;、思路 从左到右&#xff1a; 1、第一个与第二个比较&#xff0…

Ceph入门到精通-Ceph之对象存储网关RADOS Gateway(RGW)

一、Ceph整体架构及RGW在Ceph中的位置 1.Ceph的整体架构 Ceph是一个统一的、分布式的的存储系统&#xff0c;具有优秀的性能、可靠性和可扩展性。Ceph支持对象存储&#xff08;RADOSGW&#xff09;、块存储&#xff08;RBD&#xff09;和文件存储&#xff08;CephFS&#xff…