基于哈希表的用户管理系统
三大模块:
- 哈希表模块
哈希函数
哈希表创建
哈希表销毁
- 用户管理模块
显示
增
删
改
查
- 文件模块
从文件导入用户信息
将用户信息导出至文件
1.哈希函数
//hash函数(质数除余法)
int Hash_Fun1(data_type key){int pos = key%P;return pos;}
主要思想是将待哈希的数据转化为一个整数,
然后将该整数与一个质数进行取余运算,
最终得到的余数就是该数据的哈希值。
具体实现流程如下:
-
定义一个质数p,通常取一个较大的质数,以尽量减少哈希冲突的概率。
-
对于待哈希的数据,例如一个字符串,可以将其转化为一个整数,一般使用ASCII码的值或Unicode码的值等方式将字符串转换为整数。
-
将该整数与质数p进行取余运算,得到的余数即为该数据的哈希值。
优点:简单、计算速度快,适用于键值分布比较均匀的场景,
但如果哈希表中数据分布不均匀,容易导致哈希冲突,影响哈希表的性能。
//改进质数取余法(字符串转为哈希值)
int Hash_Fun(char* str){int hash = 0;while(*str){hash = hash * 31 + (*str++);}return hash%HASH_SIZE;
}
遍历字符,将其ASCII码值乘以质数31,
再加上前面所有字符的哈希值,
最后再对哈希值进行取余运算,得到最终的哈希值。
使用质数31作为哈希函数的乘数
可以使得哈希函数更加均匀地分布哈希值,避免冲突的概率更小。
31是一个质数,它的二进制表示为0001 1111。
乘以31相当于将数值左移5位再减去原值,即x * 31 = (x << 5) - x。
因此,使用31作为乘数可以有效地减少乘法操作的计算量,同时还能够使得哈希值更加分散。
2.创建哈希表
//创建哈希表
Hash* Creat_Hash(void){//1.定义Hash表的头,存储哈希表首地址和尺寸Hash* pHash = (Hash*)malloc(sizeof(Hash));if(NULL == pHash){perror("malloc error");return NULL;}memset(pHash, 0, sizeof(Hash));//2.pHash的两个成员需分别赋值//arr是一个指向数组的指针,数组名也是数组首地址,所以是**//本身的数据类型是LinkNode**,指向LinkNode*pHash->arr = (LinkNode**)malloc(sizeof(LinkNode*)*HASH_SIZE);//此处要对空间初始化,否则显示时会段错误,因为只要非NULL,//就会访问结构体信息,而此时结构体是NULLmemset(pHash->arr, 0, sizeof(LinkNode*)*HASH_SIZE);if(NULL == pHash->arr){perror("malloc error");return NULL;}pHash->count = HASH_SIZE;return pHash;
}
首先,申请一片空间(Hash* pHash)用于存储哈希表所在位置的首地址,
以及定义变量(int count)存储哈希表中链表的个数。
而后,再申请一片连续的空间存储链表首结点的地址,
连续空间大小为HASH_SIZEsizeof( LinkNode ),
HASH_SIZE是存储链表的数量。
最后,给结构体中的count赋值为HASH_SIZE。
3.销毁哈希表
//销毁所有用户信息,传入哈希表首地址的地址
int Destroy_Hash(Hash** pHash){//1.判断入参空否if(NULL == *pHash){return NULL_ERROR;}//2.遍历哈希表int i = 0;for( i=0; i<HASH_SIZE; i++ ){LinkNode* pTemp = (*pHash)->arr[i]; //定义临时指针来遍历哈希表while(pTemp != NULL){LinkNode* pDel = pTemp; //删除指针来删除pTemp = pTemp->pNext; //头删法,先连free(pDel); //后释放}}free((*pHash)->arr); //释放存储顺表地址的空间(*pHash)->arr = NULL; free(*pHash); //释放哈希表*pHash = NULL;return OK;
}
哈希表申请了三次空间,
第一次是哈希表本身,
第二次是顺表的空间,
第三次是添加新用户时,申请新结点。
释放空间时,要确保申请每个空间都被释放了。
因此,对顺表遍历的同时,也要对后面的链表遍历。
由于顺表的大小是确定的,用for循环遍历,
而链表则用while,定义一个临时指针来遍历、释放。
执行完嵌套的循环遍历后,
最后释放pHash->arr和pHash。
4.显示用户信息
//显示哈希表
int Display_Hash (Hash* pHash){//1.入参是否存在if(NULL == pHash){return NULL_ERROR;}//2.遍历显示printf("**************用户信息****************\n");int i = 0, j=1;for( i=0; i<HASH_SIZE; i++ ){LinkNode* pNode = pHash->arr[i];while(pNode != NULL){if( pNode->data.name!=NULL && pNode->data.password && pNode->data.mail){printf("用户%d:%s\t 密码:%s\t邮箱:%s\n", j++, \pNode->data.name, pNode->data.password, \pNode->data.mail);pNode = pNode->pNext;}}}printf("显示完毕!\n");return OK;
}
5.增加用户信息
//插入哈希表
int Insert_Hash(Hash* pHash, User_Inf item){//1.判断Hash表是否存在if(NULL == pHash){return NULL_ERROR;}//2.查找hash,若存在,返回无需插入if(Search_Hash(pHash, item.name) != NULL){printf("已存在,无需插入!\n");return NULL_ERROR;}//3.创建新节点pNew,并存入数据LinkNode* pNew = (LinkNode*)malloc(sizeof(LinkNode));if(NULL == pNew){perror("malloc error");return MALLOC_ERROR;}memset(pNew, 0, sizeof(LinkNode));pNew->data = item;//结构体可以直接赋值?//4.通过hash函数获得要存入的位置int pos = -1;pos = Hash_Fun(item.name);//5.将新节点,以头插方式插入hash表pNew->pNext = pHash->arr[pos];pHash->arr[pos] = pNew;return OK;
}
首先,调用查找函数,如果找到了,说明链表中有该用户,无需添加;
如果没有,则创建新结点,将用户信息存入后,
调用哈希函数获取存入的位置,采用头插法添加用户信息。
6.删除用户信息
//删除哈希表
int Del_Hash(Hash* pHash, char* user_name){//1.判断入参空否if(NULL == pHash){return NULL_ERROR;}//2.获得哈希值int pos = Hash_Fun(user_name);//3.遍历该位置上的链表LinkNode* pDel = pHash->arr[pos];//删除指针指向第一个结点LinkNode* pPre = NULL;//前继指针指向第二while(pDel != NULL ){if( 0 == strcmp(pDel->data.name, user_name) ){break;}pPre = pDel;pDel = pDel->pNext;}//4.若pDel为空,则要删除的不存在if(NULL == pDel){return NULL_ERROR;}//5.若pPre为空,说明找到的是第一个节点//否则,改变前一个节点的pNextif(NULL == pPre){pHash->arr[pos] = pDel->pNext;}else{pPre->pNext = pDel->pNext;}//打印删除结点信息printf("删除的是:\n");printf("用户名:%s\t密码:%s\t邮箱:%s\n", \pDel->data.name, pDel->data.password, pDel->data.mail);//6.释放结点free(pDel);pDel = NULL;return OK;
}
首先,定义两个指针,
LinkNode* pDel指向链表首节点,也就是pHash->arr[pos]中的地址(可能为NULL),
LinkNode* pPre是前驱结点,初始化为NULL。
而后,利用这两个指针对链表进行遍历:
从首结点开始strcmp需要的用户名,如果找到就跳出循环,
否则两个指针依次向后移动。
跳出循环有三种情况:
- pDel为空,也就是没有该用户,退出程序。
- 哈希值位置的第一个节点就是,pPre没有移动,还是NULL,此时用头删法进行删除操作,即顺序表中元素指向找到节点的下一节点(可能为空),再free。
- 哈希值位置的链表有N个节点,在链表中间,或最后找到了,此时pDel指向的是需要删除的用户。先将pPre指向pDel后的结点,再free(pDel),是最后一个结点也不影响
7.修改用户信息
//修改用户信息
int Modify_Hash(Hash* pHash, char* Name){//1.判断入参空否if(NULL == pHash || NULL == Name){return NULL_ERROR;}//2.获取哈希值int pos = Hash_Fun(Name);//3.通过顺序表找到用户LinkNode* pTemp = pHash->arr[pos];while(pTemp != NULL){if( 0 == strcmp(pTemp->data.name, Name) ){printf("该用户信息如下:\n");printf("%s ", pTemp->data.name);printf("%s ", pTemp->data.password);printf("%s ", pTemp->data.mail);// Del_Hash(pHash, pTemp->data.name);printf("\n输入新的用户信息\n");scanf("%s", pTemp->data.name);scanf("%s", pTemp->data.password);scanf("%s", pTemp->data.mail);//更改信息后,要将新的用户信息放到对应位置,并删除就结点,否则显示还会显示// Insert_Hash(pHash, pTemp->data);return OK;}pTemp = pTemp->pNext;}//4.若pTemp为空,则未找到该用户if(NULL == pTemp){return EMPTY;}return -1;
}
首先,调用哈希函数获取存储该用户信息的位置,
而后定义一个临时指针用于遍历链表,
如果找到,提示输入新信息;
如果没找到,指针向后移动;
直到指向NULL,说明没有该用户。
8.查找某用户
//查找哈希表
LinkNode* Search_Hash(Hash* pHash, char* user_name){//1.判断hash表是否存在if(NULL == pHash){return NULL;}//2.通过hash函数获得user_name所在位置posint pos = Hash_Fun(user_name);//3.通过pos获得链表中的首地址LinkNode* pTemp = pHash->arr[pos];//4.遍历链表至找到该值while(pTemp != NULL){if( 0 == strcmp(user_name, pTemp->data.name) {return pTemp;}pTemp = pTemp->pNext;}return NULL;
}
首先,调用哈希函数获取存储该用户信息的位置,
而后定义一个临时指针用于遍历链表,
如果找到,返回该指针;
如果没找到,指针向后移动;
直到指向NULL,说明没有该用户。
9.导出用户信息
//导出信息
int Output_Data(Hash* pHash, char* filename){//1.判断入参空否if(NULL == pHash || NULL == filename){return NULL_ERROR;}//2.打开文件FILE* fp = fopen(filename, "w");if(NULL == fp){perror("open error");return NULL_ERROR;}//3.遍历哈希表,存入信息int i = 0;for( i=0; i<HASH_SIZE; i++){LinkNode* pTemp = pHash->arr[i];while(pTemp != NULL){User_Inf user = pTemp->data;fprintf(fp, "用户名:%s 密码:%s 邮箱:%s\n", \user.name, user.password, user.mail);pTemp = pTemp->pNext;}}fclose(fp);return OK;
}
遍历整个哈希表,将其中的信息输出至指定文件中,
首先以只写方式打开指定文件,
如果不存在就创建一个,
每次写入信息时,清空上次的内容。
遍历每个结点时,调用fprintf函数将该名用户的信息输出至文件中,
末尾加上”\n”,最后关闭文件。
10.导入用户信息
//导入信息
int Input_Data(Hash* pHash, char* filename){//1.判断入参空否if(NULL == pHash || NULL == filename){return NULL_ERROR;}//2.打开文件FILE* fp = fopen(filename, "r");if(NULL == fp){perror("open error");return OPEN_ERROR;}//3.读取文件信息,文件中每行一个用户,信息间用空格隔开int MAX = 70;char Line[MAX];//缓冲区:存储按行读的信息while( fgets(Line, MAX, fp)){//从文件读取不为空,就一直取char Name[N];char Password[N];char Mail[N];//如果解析不出3个字符串,结束本次循环,继续下一次if( sscanf(Line, "%s %s %s",Name, Password, Mail) != 3){continue;}User_Inf user;strcpy(user.name, Name);strcpy(user.password, Password);strcpy(user.mail, Mail);//4.插入信息int ret = Insert_Hash(pHash, user);if(OK != ret){fclose(fp);return ret;}}fclose(fp);return OK;
}
首先,打开一个文件,调用fgets函数按行读取文件内容,
再调用sscanf将读取到的每行内容解析为三个字符串,
分别为name, password, mail,
并赋值给一个定义的结构体,
而后,调用增加用户函数将结构体信息添加到哈希表中,
如果插入错误就关闭文件。
相关文章:
基于哈希表的用户管理系统
三大模块: - 哈希表模块 哈希函数 哈希表创建 哈希表销毁 - 用户管理模块 显示 增 删 改 查 - 文件模块 从文件导入用户信息 将用户信息导出至文件 1.哈希函数 //hash函数(质数除余法) int Hash_Fun1(data_type key){int pos key%P;…...
GO数组切片-线性数据结构
数据结构 类型 什么是类型 ? 内存中的二进制数据本身没有什么区别,就是一串0或1的组合。 内存中有一个字节内容是0x63,他究竟是深恶 字符串?字符?还是整数? 本来0x63表示数字 但是文字必须编码成为0和1的组合 才能记…...
C++ STL学习之【优先级队列】
✨个人主页: 北 海 🎉所属专栏: C修行之路 🎃操作环境: Visual Studio 2019 版本 16.11.17 文章目录 🌇前言🏙️正文1、优先级队列的使用1.1、基本功能1.2、优先级模式切换1.3、相关题目 2、模拟…...
keepalived脑裂现象
Keepealived最常见的问题是会出现脑裂现象: Master一直发送心跳消息给backup主机,如果中间的链路突然断掉,backup主机将无法收到master主机发送过来的心跳消息(也就是vrrp报文),backup这时候会立即抢占mas…...
[stable-diffusion-art] 指北-1
https://stable-diffusion-art.com/beginners-guide/https://stable-diffusion-art.com/beginners-guide/ Stable Diffusion教程目录 - 知乎按: 这个外国教程站中的文章太好了,数量适当,质量很高可惜博文只能按时间浏览,所以整理…...
「C/C++」C/C++预处理器
博客主页:何曾参静谧的博客 文章专栏:「C/C」C/C学习 目录 一、宏替换 #define1. 定义常量2. 定义函数3. 定义代码块 二、条件编译 #if1. 使用 #ifdef、 #else 和 #endif2. 使用 #if 、#elif、#else和 #endif 编译不同版本的代码3. 使用 #ifndef 和 #def…...
java语言入门教程文章
好的,以下是Java语言入门教程: Java是一种高级编程语言,由Sun Microsystems于1995年推出。Java语言具有良好的可移植性和安全性,因此被广泛应用于Web应用程序、移动应用程序、企业应用程序等各个领域。本教程将带领初学者快速入门…...
基于灰狼算法的极限学习机(ELM)回归预测-附代码
基于灰狼算法的极限学习机(ELM)回归预测 文章目录 基于灰狼算法的极限学习机(ELM)回归预测1.极限学习机原理概述2.ELM学习算法3.回归问题数据处理4.基于灰狼算法优化的ELM5.测试结果6.参考文献7.Matlab代码 摘要:本文利用灰狼算法对极限学习机进行优化,并…...
【五一创作】ERP实施-委外业务-委外采购业务
委外业务主要有两种业务形态:委外采购和工序外协,委外采购主要是在MM模块中实现,工序外协主要由PP模块实现,工序外协中的采购订单创建和采购收货由MM模块实现。 委外采购概念 委外采购,有些企业也称为带料委外或者分包…...
DAY 54 数据库基础
数据库的基本概念 数据(Data): 描述事务的符号记录包括数字、文字、图形、图像、声音、档案记录以”记录“形式按统一的格式进行存储 表: 将不同的记录组织在一起用来存储具体数据 数据库: 表的集合,…...
网络编程 总结二
一、TCP TCP模型 1. TCP搭建相关函数: 套接字Socket 1)Socket函数: 2)bind 3)listen 4)accept 5)recv 注意: 1> TCP中的recv 可以替换成read; 2>TCP中的…...
消息称苹果Type-C口充电未设MFi限制,iOS17将更新Find My服务
根据国外科技媒体 iMore 报道,基于消息源 analyst941 透露的信息,苹果公司目前并未开发 MFi 限制。 根据推文信息内容,两款 iPhone 15 机型的最高充电功率为 20W,而 iPhone 15 Pro 机型的最高支持 27W 充电。 此前古尔曼表示苹…...
设计模式——工厂模式(简单工厂、工厂方法、抽象工厂)
是什么? 工厂模式的目的是将创建对象的具体过程隐藏起来,从而达到更高的灵活性 工厂模式分为:简单工厂模式、工厂方法模式、抽象工厂模式; 为什么? 在Java中,万物皆是对象,我们在使用的时候…...
《C语言技术体系》 学习路线总目录 + 思维导图
目录 前言 正文 思维导图 第1章 流程结构 1.1 初识C语言 1.2 流程结构 1.3 数据类型 1.4 运算符表达式 第2章 指针与数组 2.1 指针基本概念 2.2 一维数组 2.3 二维及多维数组 2.4 指针与数组 第3章 模块化重构 3.1 函数 3.2 typedef类型定义 3.3 enum枚举 3.…...
数字图像处理简答题
目录 1.人类视觉对颜色的主观感觉包括哪三类? 2. 图像成像的过程包括哪三步? 3.图像的采样和量化分别指什么? 4、取k8时,将下图用相应矩阵表示 5、简述当限定了数字图像的数据量时采样和量化参数的选择遵循哪两条原则&#x…...
【Java校招面试】基础知识(五)——GC
目录 前言一、基础概念二、垃圾回收算法三、垃圾收集器四、引用后记 前言 本篇主要介绍Java垃圾回收机制——GC的相关内容。 “基础知识”是本专栏的第一个部分,本篇博文是第五篇博文,如有需要,可: 点击这里,返回本专…...
使用CMake调用Makefile 项目
目录标题 基本示例Cmake传递lib给MakefileCmake传递参数给Makefile,比如make cleanWindows下使用Cmake调用Makefile 基本示例 如果项目是使用传统的Makefile构建的,并且您希望使用CMake调用这些Makefile,您可以使用CMake的add_custom_target…...
快速傅里叶变换FFT学习笔记
点值表示法 我们正常表示一个多项式的方式,形如 A ( x ) a 0 a 1 x a 2 x 2 . . . a n x n A(x)a_0a_1xa_2x^2...a_nx^n A(x)a0a1xa2x2...anxn,这是正常人容易看懂的,但是,我们还有一种表示法。 我们知道…...
如何下载安装驱动
1 打开浏览器 这里以Edge浏览器举例 第一步打开桌面上的Edge浏览器 如果您的桌面上没有 那么找到搜索栏 搜索Edge 然后打开 打开之后一般是这样 然后把我发送您的地址 驱动下载地址 https://t.lenovo.com.cn/yfeyfYyD (这个网址只是一个例子) 删除掉前…...
鸿蒙Hi3861学习四-Huawei LiteOS介绍
一、什么是LitesOS Huawei LiteOS是华为针对物联网领域推出的轻量级物联网操作系统,是华为物联网战略的重要组成部分,具备轻量级、低功耗、互联互通、组件丰富、快速开发等关键能力。基于物联网领域业务特征打造领域性技术栈,为开发者提供“一…...
Vue核心 收集表单数据 过滤器
1.14. 收集表单数据 收集表单数据: 若: ,则v-model收集的是value值,用户输入的就是value值。若: ,则v-model收集的是value值,且要给标签配置value值。若: 没有配置input的value属性,那么收集的就是checked(勾选 or 未…...
华为EC6108V9E/EC6108V9I_rk3228_安卓4.4.4_通刷_卡刷固件包
华为EC6108V9E/EC6108V9I_rk3228_安卓4.4.4_通刷_卡刷固件包-内有教程 特点: 1、适用于对应型号的电视盒子刷机; 2、开放原厂固件屏蔽的市场安装和u盘安装apk; 3、修改dns,三网通用; 4、大量精简内置的…...
数字化转型导师坚鹏:面向数字化转型的大数据顶层设计实践
面向数字化转型的大数据顶层设计实践 课程背景: 数字化背景下,很多企业存在以下问题: 不清楚大数据思维如何建立? 不清楚企业大数据分析方法? 不了解大数据应用成功案例? 课程特色: …...
day27_mysql
今日内容 零、 复习昨日 一、单表查询 二、多表联查 零、 复习昨日 1 DDL,DML,DQL是啥 DDL 数据定义语言(库,表,列)DML 数据操作语言(表内数据的操作增删改)DQL 数据查询语言(表内数据的查询&am…...
QwtPlotCurve使用说明
QwtPlotCurve是Qwt库中用于绘制曲线的类,可以在QwtPlot上绘制各种类型的曲线,如折线、样条线、散点等。以下是QwtPlotCurve的一些常用函数和使用说明: setSamples(const QPolygonF &samples):设置曲线的数据点,参数…...
JS逆向 -- 某平台登录加密分析
一、打开网站,使用账号密码登录 账号:aiyou123.com 密码:123456 二、通过F12抓包,抓到如下数据,发现密码加密了 三、加密结果是32位,首先考虑是md5加密。 四、全局搜索pwd,点击右上角…...
一分钟快速实现Flask框架的蓝图和视图
一分钟快速实现Flask框架的蓝图和视图 Flask是一个轻量级的Web应用框架,非常适合快速开发小型的Web应用。Flask框架使用蓝图(Blueprint)和视图(View)的概念来组织应用程序的代码。在本文中,我们将介绍如何…...
Mysql 约束练习【第13章_约束】
#第13章_约束 /* 基础知识 1.1 为什么需要约束? 为了保证数据的完整性! 1.2 什么叫约束?对表中字段的限制。 1.3 约束的分类: 角度1:约束的字段的个数 单列约束 vs 多列约束 角度2:约束的作用范围 列…...
java调用cmd命令
1.首先,我们需要了解一下 java是如何调用 cmd的: 6.在实际的开发中,我们有可能会遇到 java调用 cmd命令的情况: 7.对于一些特定的环境下,例如在嵌入式系统中,那么我们可以使用下面这种方式来调用 cmd命令&a…...
Qt音视频开发36-超时检测和自动重连的设计
一、前言 如果网络环境正常设备正常,视频监控系统一般都是按照正常运行下去,不会出现什么问题,但是实际情况会很不同,奇奇怪怪七七八八的问题都会出现,就比如网络出了问题都有很多情况(交换机故障、网线故障、带宽故障等),所以监控系统在运行过程中,还得做超时检测,…...
阜新网站建设公司/ps培训
Set 属性: Set.prototype.constructor:Set.prototype.size:方法 操作方法 adddeletehasclearArray.from()可以将Set结构转为数组遍历方法 keys():返回键名的遍历器,结果与values()相同values(): 返回键值的遍历器entries():返回键值对的遍历器forEach(value…...
如何用服务器做网站/品牌策划方案怎么写
这是基于java的电子邮件系统--工具软件下载,基于java开发的邮件系统,包括基本的邮件收发,附件功能-Java-based development of e-mail system, including the basic send and receive mail, attachments.软件介绍基于j…...
展览设计制作公司/北京百度搜索排名优化
最常用的Eclipse的快捷键,各个实用,由Java攀登网提供。 虽然现在IDEA非常的好用,但是Eclipse还是有一定的受众用户,包括我现在的公司,领导就是让用Eclipse,因此喜欢Eclipse的朋友可以看下。 后面可以再讲…...
阿里云虚拟主机怎么建立网站/免费个人网站模板
知己知彼才能百战不殆。要想回答好问题就要先思考面试官的提问的动机。 首先我们分析一下面试官为什么要问这个问题,通过这个问题的答案他希望能获取到什么信息,然后我们把他希望获取到信息表达出来就可以了。面试官通过这个问题主要想了解三个方面&…...
设计师培训机构有哪些/开源seo软件
译自Matthew的《A Good Part-of-Speech Tagger in about 200 Lines of Python》,本文用最精简的代码演示了如何写一个基于感知机的高性能词性标注器。以下是正文:自然语言处理的最新技术大部分都停留在学术界,但学术界往往非常谨慎、不愿意把…...
流量对网站排名的影响因素/软文写作要求
本节重点介绍jquery中调用ajax的4种方法:$.get、$.post、$getJSON、$ajax。如果读者没有javascript和jquery的知识,或没有ajax的概念,请先参阅下本站的javascript教程与jquery教程栏目中的相关内容。1、$.get$.get()方法使用GET方式来进行异步…...