散列表又称为哈希表(Hash Table), 是为了方便查找而生的数据结构。关于散列的表的解释,我想引用维基百科上的解释,如下所示:

散列表Hash table,也叫哈希表),是根据(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表

散列表的创建就是将Value通过散列函数和处理散列key值冲突的函数来生成一个key, 这个key就是Value的查找映射,我们就可以通过key来访问Value的值。本篇博客我们就来好好的聊一下散列表的实现,当然主要还是构建散列函数还有解决冲突的函数,下方我们先给出散列函数为“除留取余法”和处理冲突的线性探测发的原理图,然后再给出面向对象的实现,最后在给出相应的代码实现。

一、散列表创建原理

本部分我们将以一系列的示意图来看一下如何来创建一个哈希表,我们就将下方截图中的数列中的数据来存储到哈希表中。在下方的实例中,我们采用除留取余法来创建value的映射key, 如果产生冲突,就采用线性探测法来处理key的冲突。下方就是我们要构建哈希表的数据以及所需的散列函数和处理冲突的函数。

  

1.散列表的构建

接下来我们就要将上述元素插入到我们的散列表中,下方是对每个步骤的描述:

  • 将62插入到散列表中,通过取余求出的key为7。散列表中7的位置没有存入东西,所以62的key为7。同理依次插入88,58.

  • 然后计算47的key值,通过除留取余法,得到47%11 = 3, 发现3已经存储了58,也就是说与58的key冲突了,于是乎进行一轮冲突的解决key = key + 1 = 4。4的位置没有存入值,所以讲47存入4的位置。

  • 按上述两个步骤,将剩下的值插入到HashTable中即可,下方是完整的步骤。

  

2、散列表的查找

散列表的查找与散列表元素的插入是非常相似的,也是通过哈希函数以及处理冲突的方法来完成的。我们以在创建好的查找表中查找93为例,首先通过创建哈希表时使用的哈希函数来计算93对应的key, key = 93 % 11 = 5。然看key = 5所映射的数据,我们发现HashTable[5] = 37, 而不是93。那么说明93在插入的时候遇到了冲突,然后通过冲突的处理后才插入到HashTable中的。所以需要通过冲突处理函数找到93正确的key。进行一轮冲突处理,即为key = key + 1 = 5 + 1 = 6。我们发现hashTable[6] = 93, 于是乎我们找到93的下标是6。

上述这种查找方式,与我们之前聊的顺序查找、二分查找等等效率要高的多,不过散列函数和处理冲突的函数的选择在提高查找效率方面是至关重要的。查找顺序如下:

  

二、散列表的具体代码实现

聊完原理,接下来就到了我们代码实现的时刻了。下方我们会使用面向对象语言Swift来实现我们的HashTable。因为散列表由于散列函数与处理冲突函数的不同可以分为多种类型,但是每种类型之前的区别除了散列函数和冲突函数不同之外,其他的还是完全一致的,因为我们使用的是面向对象语言,所以我们可以将相同的放在父类中实现,而不同的则由子类提供。下方代码的实现,主要也是这个思路。

1.抽象HashTable的父类

下方这个HashTable的类,就是我们抽象的散列表的父类。该类所扮演的角色类似于接口的角色,定义了对外的调用方式,并且给出了散列表共用方法的实现。其实下方这个类与C++中的虚基类极为相似。我们采用Swift中的字典来充当我们的HashTable, 字典的Value就是我们要插入的值,而字典的key就是通过插入的值Value生成的并处理完冲突的key。

下方代码中的hashTable字典中存储的就是我们的散列表。计算属性count中存储的就是散列表的大小。而list数组中存储的就是要插入到散列表中的数据。每个方法所表达的功能请看下方截图中的注释,如下所示。

在HashTable方法中,有两个方法需要注意一下。一个是hashFunction()方法,另一个就是conflictMethod()方法。这两个方法需要在散列表的子类中进行重写的,hashFunction()方法用来提供散列函数,而conflictMethod()则用来提供处理key值冲突的方法。因为散列函数有许多种,而处理冲突的方法也有许多种,所以我们可以将其放到具体的子类中去实现。不同类型的散列表中这两个方法给出具体的散列函数和处理冲突的方法。

  

2.除留取余法与线性探测

接下来我们要给出散列函数为“除留取余法”以及使用线性探测的方式来处理冲突的散列表。该散列表我们命名为HashTableWithMod, 当然该类是继承自上面的散列表父类HashTable的,并且重写了hashFunction()和conflictMethod()方法。在相应的方法中给出了相应的解决方案。

  

3.直接定址法与随机数探测法

与上面的HashTableWithMod类类似,我们还可以继承自HashTable类给出哈希函数为直接定址法,以及使用随机数探测法来处理冲突的散列表。当然也需要将hashFunction()和conflictMethod()这两个方法进行重写了。具体代码如下所示:

  

当然,上面只给出了部分哈希函数的实现和处理冲突的方式,其余的在本篇博客中就不做过多赘述了,请自行Google。

三、测试用例

接下来又到了我们测试的时刻了,上方我们依然采用“面向接口”编程的思想来实现的,所以我们的测试用例可以使用一个。将不同类型的HashTable的对象即可,下方就是我们的测试用例。

  

下方是对除留取余法+线性探测的哈希表进行的的测试结果。上面是使用该方法创建哈希表的详细步骤,然后将创建好的hashTable进行了输出,最后给出了查找的结果。如下所示:

  

上方是构建哈希表的整个过程,下方则是将创建好的HashTable进行输出,并且给出35的查询结果:

  

今天的博客就先到这,更详细的代码实现请移步github分享链接,如下所示。

github分享链接:https://github.com/lizelu/DataStruct-Swift/tree/master/HashTableSearch

算法与数据结构(十二) 散列(哈希)表的创建与查找(Swift版)的更多相关文章

  1. Java数据结构与算法解析(十二)——散列表

    散列表概述 散列表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值. 散列表的思路很简单,如果所有的键都是整数,那么就可以使用一个简单 ...

  2. Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

    Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin[Finchley 版]  发表于 2018-04-24 |  随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请 ...

  3. oracle的散列聚簇表

    在簇表中,Oracle使用存储在索引中的键值来定位表中的行, 而在散列聚簇表中,使用了散列函数代替了簇索引,先通过内部函数或者自定义的函数进行散列计算,然后再将计算得到的码值用于定位表中的行. 创建散 ...

  4. 第二十二章 Django会话与表单验证

    第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...

  5. 【数据结构】之散列链表(Java语言描述)

    散列链表,在JDK中的API实现是 HashMap 类. 为什么HashMap被称为“散列链表”?这与HashMap的内部存储结构有关.下面将根据源码进行分析. 首先要说的是,HashMap中维护着的 ...

  6. js数据结构之hash散列的详细实现方法

    hash散列中需要确定key和value的唯一确定关系. hash散列便于快速的插入删除和修改,不便于查找最大值等其他操作 以下为字符和数字的hash散列: function HashTable () ...

  7. 算法与数据结构(十) 二叉排序树的查找、插入与删除(Swift版)

    在上一篇博客中,我们主要介绍了四种查找的方法,包括顺序查找.折半查找.插入查找以及Fibonacci查找.上面这几种查找方式都是基于线性表的查找方式,今天博客中我们来介绍一下基于二叉树结构的查找,也就 ...

  8. 哈希--Hash,“散列”/“哈希”

    哈希 Hash,翻译“散列”,音译为“哈希”,把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散 ...

  9. 线性探测再散列 建立HASH表

    根据数据元素的关键字和哈希函数建立哈希表并初始化哈希表,用开放定址法处理冲突,按屏幕输出的功能表选择所需的功能实现用哈希表对数据元素的插入,显示,查找,删除. 初始化哈希表时把elem[MAXSIZE ...

随机推荐

  1. 如何kill掉tomcat服务

    一般我们使用tomcat使用的都是8080端口,今天我在使用eclipse的时候连续启动了两次tomcat,发现被占用了. 第一想法便是重启eclipse,重启后发现tomcat的8080仍然是已经被 ...

  2. MySQL ERROR 1054(42S22)

    修改用户的密码,网上搜到的命令为如下 执行后报错 ERROR 1054(42S22) Unknown column 'password' in ‘field list’ 错误的原因是 5.7版本下的m ...

  3. 个人NABCD

    采用NABCD模型对我们的团队项目大学生失物招领平台进行了详细的需求分析说明,其中N指(Need需求),A (Approach 做法),B (Benefit好处),C (Competitors 竞争) ...

  4. Git初始配置和基本使用

    初次运行Git前的配置 本文是在安裝完git以后首先应做到一些配置,安装教程可以参考廖雪峰git教程 用户信息 当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址. 这样做很重要,因为每 ...

  5. vertx模块HAManager高可用

    HAManager public HAManager(VertxInternal vertx, DeploymentManager deploymentManager, ClusterManager ...

  6. python-shutil学习

    shutil:高级的 文件.文件夹.压缩包 处理模块 1. shutil.copyfileobj(fsrc, fdst[, length])(copyfileobj方法只会拷贝文件内容)将文件内容拷贝 ...

  7. pwn学习(1)

    0x00 简介 入职之后,公司发布任务主搞pwn和re方向,re之前还有一定的了解,pwn我可真是个弟弟,百度了一番找到了蒸米大佬的帖子,现在开始学习. 0x01 保护方式 NX (DEP):堆栈不可 ...

  8. 「LibreOJ Round #9」CommonAnts 的调和数

    题解: 对于subtask3:可以把相同的归在一起就是$nlogn$的了 对于subtask4: 可以使用高维前缀和的技术,具体的就是把每个质因数看作一维空间 那么时间复杂度是$\sum \limit ...

  9. sql 选取分组中的第一条,显示聚合以外的列,having字句的使用

    分组中的第一条:select * from(select row_number() over(partition by 列1,列2,... order by 列1,列2,...) as rownum ...

  10. jquery的选择器——[作为学习备用]

    1,转载:https://www.cnblogs.com/onlys/articles/jQuery.html jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $(&quo ...