哈希表算法通俗理解和实现
【坚决抵制某商业网站直接拷贝博客而不标明出处的粗暴做法,转载请标明出处,谢谢!】
顺序查表法
假设现在有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 ...
最新文章
- 负载均衡算法-最少连接数均衡
- windows 2003系统目前最完善最完美的安全权限方案(转)
- 代码同步工具_可以多重连接的数据库管理工具
- SQL Server的数据导入MySQL数据库方法简介
- ddr5内存上市时间_DDR5内存明年才能上市,SK Hynix已预研DDR6:12Gbps
- 从厕所排队引发的产品设计方案思考
- JavaScript单线程运行机制与并发模型
- 什么是抽象类?抽象类的作用_Java面试题amp;和amp;amp;的作用和区别
- 计算机组成原理fpga实验指导书,计算机组成原理 FPGA实验指导书.doc
- 软件工程概论--课后作业1
- 2018.01.07软件更新公告
- javaScript,Dwr分页模拟
- 禁忌搜索算法解决旅行商问题
- 架构系列---发号器(全局唯一ID生成器)系统设计方案和思路
- 杭州配眼镜大调研:各年龄段如何配到高性价比眼镜?去哪配镜?
- Modbus协议简单总结
- 【OpenGL ES】二维图形绘制
- 用百行Python代码写一个关于德州扑克的类
- python解析雷达数据_激光雷达数据解析(Python-lidar-data-analysis_V1.0)
- 1.0 Lua教程之基本语法
热门文章
- Redhat7/Centos7服务器设置IP地址
- 苹果截屏快捷键_Mac 有哪些冷门快捷键?
- python - 正则表达式 与或非
- C++程序设计技巧 NVI(Non-Virtual Interface )
- php生成数字订单号,php生成订单号函数
- 全方面对比流行报表开发工具,哪一个才是你的菜?
- 【文献阅读】Optimistic Bull or Pessimistic Bear: Adaptive Deep Reinforcement Learning for Stock Portfolio
- nginx lua读redis
- mysql right函数
- 程序员转行干什么好呢