哈希表算法通俗理解和实现
【坚决抵制某商业网站直接拷贝博客而不标明出处的粗暴做法,转载请标明出处,谢谢!】
顺序查表法
假设现在有1000个人的档案资料需要存放进档案柜子里。要求是能够快速查询到某人档案是否已经存档,如果已经存档则能快速调出档案。如果是你,你会怎么做?最普通的做法就是把每个人的档案依次放到柜子里,然后柜子外面贴上人名,需要查询某个人的档案的时候就根据这个人的姓名来确定是否已经存档。但是1000个人最坏的情况下我们查找一个人的姓名就要对比1000次!并且人越多,最大查询的次数也就越多,专业的说这种方法的时间复杂的就是O(n),意思就是人数增加n倍,那么查询的最大次数也就会增加n倍!这种方法,人数少的时候还好,人数越多查询起来就越费劲!那么有什么更好的解决方法吗?答案就是散列表算法,即哈希表算法。
哈希表算法
假设每个人的姓名笔划数都是不重复的,那么我们通过一个函数把要存档的人姓名笔划数转换到1000以内,然后把这个人的资料就放在转换后的数字指定的柜子里,这个函数就叫做哈希函数,按照这种方式存放的这1000个柜子就叫哈系表(散列表),人名笔画数就是哈系表的元素,转换后的数就是人名笔划数的哈希值(也就是柜子的序号)。当要查询某个人是否已经存档的时候,我们就通过哈希函数把他的姓名笔划数转化成哈希值,如果哈希值在1000以内,那么恭喜你这个人已经存档,可以到哈希值指定的柜子里去调出他的档案,否则这个人就是黑户,没有存档!这就是哈希表算法了,是不是很方便,只要通过一次计算得出哈希值就可以查询到结果了,专业的说法就是这种算法的时间复杂是O(1),即无论有多少人存档,都可以通过一次计算得出查询结果!
当然上面的只是很理想的情况,人名的笔划数是不可能不重复的,转换而来的哈希值也不会是唯一的。那么怎么办呢?如果两个人算出的哈希值是一样的,难道把他们都放到一个柜子里面?如果1000个人得出的哈希值都是一样的呢?下面有几种方法可以解决这种冲突。
开放地址法
这种方法的做法是,如果计算得出的哈希值对应的柜子里面已经放了别人的档案,那么对不起,你得再次通过哈希算法把这个哈希值再次变换,直到找到一个空的柜子为止!查询的时候也一样,首先到第一次计算得出的哈希值对应的柜子里面看看是不是你要找的档案,如果不是继续把这个哈希值通过哈希函数变换,直到找到你要的档案,如果找了几次都没找到而且哈希值对应的柜子里面是空的,那么对不起,查无此人!
拉链法(链地址法)
这种方法的做法是,如果计算得出的哈希值对应的柜子里面已经放了别人的档案,那也不管了,懒得再找其他柜子了,就跟他的档案放在一起!当然是按顺序来存放。这样下次来找的时候一个哈希值对应的柜子里面可能有很多人的档案,最差的情况可能1000个人的档案都在一个柜子里面!那么时间复杂度又是O(n)了,跟普通的做法也没啥区别了。在算法实现的时候,每个数组元素存放的不是内容而是链表头,如果哈希值唯一,那么链表大小为1,否则链表大小为重复的哈希值个数。
公共溢出区
这种方法跟拉链法也差不多,如果计算得出的哈希值对应的柜子里面已经放了别人的档案,那么就把这个人放到另外一个档案室里面哈希值对应的柜子里面,这样哈希值是一样的,但是档案室不同了。查找的时候根据得到哈希值去不同档案室分别查找,直到找到档案或者没有找到档案为止。这样其他的那些档案室就叫做公共溢出区。
通过上面的描述可以看出,所谓的哈希表算法复杂度也不一定就是理想的O(1),但即便如此,还是比普通的顺序查表法速度快多了,因为不可能所有的哈希值都是一样的,如果那样的话,只能说明你的哈希函数不够优秀,你要做的就是换一个哈希函数!一个好的哈希函数应该尽可能让要保存的内容平均的分布在哈希表上。常用的哈希函数有:直接定址法、求余法、数字分析法、平方取中法、折叠法、随机数法等。下面是最常用的求余法。
求余法哈希函数
这种方法就是用人名笔画数除以一个常数,最后的余数就是哈希值。这就是最简单也是最常用的哈希函数。当然这个常数的取法也是就讲究的,一句话,最好是一个跟2或者10的乘幂差值比较大的素数!这种方法的缺陷就是比较耗时,因为除法取余在CPU执行的时候比其他算数运算用的时钟周期更长!
C语言实现
下面是求余法哈希函数的C语言实现:
/** hashTable.c** Created on: 2016-4-8* Author: zhw123*/
#include <stdio.h>
#include <stdlib.h>#define HASHSIZE 25
#define NULLVALUE -32767typedef struct{int size;int element[HASHSIZE];
}hashStruct;/************************************************************ 哈希表初始化* 给哈希表分配内存并初始化元素为空值***********************************************************/
void hashTableInit(hashStruct **hashTable)
{*hashTable=(hashStruct *)malloc(sizeof(hashStruct));(*hashTable)->size=HASHSIZE;int i=0;for(i=0;i<HASHSIZE;i++){(*hashTable)->element[i]=NULLVALUE;}
}/************************************************************求元素哈希表地址***********************************************************/
int getHashAddress(int element)
{return element%HASHSIZE;
}/************************************************************在哈希表中插入元素*成功返回0,返回-1表示哈系表已满***********************************************************/
int insertElement(hashStruct *hashTable,int element)
{int address=getHashAddress(element);while(hashTable->element[address]!=NULLVALUE){address=getHashAddress(address+1);if(address==getHashAddress(element)){return -1;}}hashTable->element[address]=element;return 0;
}/************************************************************查询哈希表*返回匹配的哈系表地址,返回-1表示没有找到目标***********************************************************/
int searchHashTable(hashStruct *hashTable,int element)
{int address=getHashAddress(element);while(hashTable->element[address]!=element){address=getHashAddress(address+1);if(hashTable->element[address]==NULLVALUE||address==getHashAddress(element)){return -1;}}return address;
}/************************************************************在哈希表中删除元素*成功返回元素地址,返回-1表示没有该元素***********************************************************/
int removeElement(hashStruct *hashTable,int element)
{int address=searchHashTable(hashTable,element);if(address==-1){return -1;}hashTable->element[address]=NULLVALUE;return address;
}static void outTable(int *table, int lenght)
{int i=0;for(i=0;i<lenght;i++){printf("%d ",table[i]);}printf("\n") ;
}int main()
{hashStruct *hashTable;hashTableInit(&hashTable);//初始化哈希表int table[25]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25} ;int i=0;//插入元素for(i=0;i<HASHSIZE;i++){insertElement(hashTable,table[i]);}printf("HashTable: ");outTable(hashTable->element,HASHSIZE);//输出哈系表printf("Remove return : %d\n",removeElement(hashTable,22));//删除元素22printf("Remove return : %d\n",removeElement(hashTable,100));//删除没有的元素11printf("New hashTable: ");outTable(hashTable->element,HASHSIZE);//输出新的哈系表//查找元素23,打印结果int element=23;int address=searchHashTable(hashTable,element);if (address==-1){printf("Have no match !\n") ;}else{printf("%d hashAddress is : %d\n",element,address) ;}return 0 ;
}
运行结果:
程序首先初始化了一个哈希表,然后把25个数字插入哈希表并打印哈希表,再删除元素22和100并返回删除结果,删除后打印新表,最后查询元素23并打印查询结果。
哈希表算法通俗理解和实现相关推荐
- 两数相加——哈希表算法
力扣刷题总结 一.前言 二.两数相加 1.题意 2.示例 3.题目解析 4.官方题解 思路分析 哈希表算法的优势: 思路及算法: 代码分析 C语言代码实现及详细注释说明: python代码实现及详细注 ...
- 除留余数法构造哈希表_哈希表算法原理
基本概念 哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构.通过哈希表,数据元素的存放位置和数据元素的关键字之间建立起某种对应关系,建立这种对应关系的函数称为哈希函数. 哈 ...
- 迪杰斯特拉算法通俗理解
迪杰斯特拉算法 迪杰斯特拉算法是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题.主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩 ...
- JS哈希表算法——空间换时间
题目来源力扣: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素 ...
- viterbi算法通俗理解
文章目录 viterbi算法是什么 手动理解 缺点分析 算法详解 算法推论 viterbi与隐马尔可夫 隐马尔科夫链的三个基本问题 隐马尔科夫链的五元组 更详细的解释 代码实现 实现 测试 参考 vi ...
- 哈希表——算法专项刷题(五)
五.哈希表 哈希表多用于辅助记录是否存在key或者通过key找下标value 5.1插入.删除和随机访问都是O(1)的容器 原题链接 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结 ...
- viterbi,维特比算法通俗理解
维特比算法说白了就是动态规划实现最短路径,只要知道"动态规划可以降低复杂度"这一点就能轻松理解维特比算法 维特比算法是一个特殊但应用最广的动态规划算法,利用动态规划,可以解决任何一 ...
- jvm 分代回收算法通俗理解
jvm区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...
- PID算法通俗理解,平衡车,倒立摆,适合不理解PID算法的人来看!
先插句广告,本人QQ522414928,不熟悉PID算法的可以一起交流学习,随时在线(PID资料再我的另一篇博客里) 倒立摆资料连接↓ https://www.cnblogs.com/LiuXinyu ...
- 提高篇 第二部分 字符串算法 第1章 哈希和哈希表
浅谈字符串哈希_1264Ikaros的博客-CSDN博客_字符串哈希 图书管理-哈希表_handsome·wjc的博客-CSDN博客 字符串哈希 哈希表 - DTTTTTTT - 博客园 图书管理(L ...
最新文章
- Scott Mitchell的ASP.NET2.0数据指南中文版索引
- C++中bool类型变量初值对程序的影响
- [恢]hdu 2077
- Node 应用篇!推荐 10 个好用的 Node 的开源项目 YYDS
- 合并二叉树—leetcode617
- [存储引擎基础知识]InnoDB与MyISAM的六大区别(非原创)
- win32 api 文件操作!
- 狂神说Java学习笔记 Java基础
- 系统集成项目管理视频课程
- 因发生下列错误 无法创建映射网络驱动器_怎么来修复“Windows无法安装所需文件的错误原因”?...
- [转载] 机器学习之主成分分析PCA(Python实现)
- 手机号码状态检测(空号检测)的原理
- Excel的去重、分列
- 富贵论坛的来历和背景
- 算法---木头切割问题(二分法)
- 请根据以下需求使用决策表设计测试用例
- 08:go语言数字类型
- 心电电路算法滤波_一种新型心电信号滤波电路的制作方法
- 在Windows 7下删除注册表项时,权限不足
- 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析
热门文章
- html设置label的字体大小,swift - label 的font 设置 文字字体和大小
- 一个学计算机的打字速度慢,电脑打字速度慢怎么办
- Linux 怎么防止 ssh 被暴力破解
- 计算机在地理数据的应用,地理信息系统(GIS)在环境监测中的应用
- 各种数据库中的时间日期类型
- 发布订阅模式(一):tiny-emitter
- 2022年房地产市场趋势展望
- SpringMVC上传图片报400
- 迪士尼源码下载站_如何下载迪士尼+电影和电视节目以离线观看
- html实现银行卡中间四位显示为*号,银行卡和手机号中隐藏的数字用*号代替,不能和文字垂直居中,有什么解决方案吗?...