散列表又称为哈希表(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. MapReduce流量统计

    准备数据access.log 要用到的只有第二个手机号,倒数第三上行流量,倒数第二下行流量. 1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120. ...

  2. C#冒泡法排序源码

    如下内容内容是关于C#冒泡法排序的内容,应该对码农有一些用途. int[] myArray = new int[] { 10, 8, 3, 5, 6, 7, 4, 6, 9 }; for( int j ...

  3. Linux cpu 内存 压力测试

    stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10s

  4. 第三次java作业

    编写“学生”类及其测试类. 5.1 “学生”类: ² 类名:Student ² 属性:姓名.性别.年龄.学号.5门课程的成绩 ² 方法1:在控制台输出各个属性的值. ² 方法2:计算平均成绩 ² 方法 ...

  5. PHP数组笛卡尔积组合排列

    /*方法一*/ function descartes() { $t = func_get_args(); if (func_num_args() == 1) { return call_user_fu ...

  6. notes for lxf(二)

    函数 abs()绝对值 max()返回最大值 raise 后接异常类 引发异常 函数返回多个值其实就是返回一个tuple 函数默认返回None 如果有必要检查参数类型用isinstance() typ ...

  7. SQL Server Governer 控制资源的使用

    --- Create a resource pool for production processing  --- and set limits.  USE master;  GO  CREATE R ...

  8. 实验5 Spark SQL 编程初级实践

    源文件内容如下(包含 id,name,age),将数据复制保存到 ubuntu 系统/usr/local/spark 下, 命名为 employee.txt,实现从 RDD 转换得到 DataFram ...

  9. 多人合作项目如何去管理git仓库

    前记:在git之前依稀记得有SVN去管理代码仓库,现在多用git去管理我们的代码:现在一般的项目大多数是多人同时开发,这样就会存在一个问题就是如何去协调开发:这也是lz当前使用git开发管理的些许经验 ...

  10. Android常规布局方式和方法

    一.关于给控件添加ID属性 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xm ...