散列表的一些基本概念

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

计算映射位置的函数叫做 散列函数

存放记录的数组叫做 散列表

散列函数可能会把两个或两个以上的不同关键字映射到同一个散列表中的位置,这种情况叫做 冲突

一些散列函数计算的key值会使得大量元素出现在相邻的散列地址上,从而大大降低了查找效率,这种现象叫做 “聚集”(或堆积)

装填因子,定义为一个表的装满程度,公式为
α=n散列表长度m(n=表中的记录数)α = \frac{n}{散列表长度m}(n=表中的记录数) α=散列表长度mn​(n=表中的记录数)
散列表的平均查找长度依赖于散列表的装填因子 α,而不直接依赖于 n 或 m。装填因子有这样的性质,直观地看,α越大,表示装填的记录约 “满”,发生 冲突 的可能性越大,反之发生 冲突 的可能性 越小

散列函数的构造方法

设计散列函数的几个原则

  1. 函数的定义域必须包含全部关键字
  2. 计算出来的key应该是等概率、均匀的
  3. 尽量简单

1. 直接定址法
H(key)=a∗key+b(a,b∈R)H(key) = a * key + b (a,b∈R) H(key)=a∗key+b(a,b∈R)
特点:简单不会产生冲突

适用:是和关键字的分布基本连续

缺点:若关键字分布不连续,会造成较多空位,从而浪费存储空间

例如:

有表一数据如下,经过 直接定址法 处理后的hash表如表二所示

表一

地址 A1 A2 …… A99 A100
年龄 1 2 …… 99 100
人数 980 800 …… 495 107

表二

地址 A0 A1 …… A99 A100
年龄 1980 1981 …… 1999 2000
人数 980 800 …… 495 107

2. 除留余数法
H(key)=key%p(p≤m)H(key) = key \% p (p≤m) H(key)=key%p(p≤m)
特点:p是一个不大于散列表长度m且最接近或等于散列表长度的质数

适用:使得每个关键字通过该函数转换后等概况地映射到散列空间上的任一地址,从而降低冲突

例如:

已知待散列元素为(18756043549046),表长m=10p=7,则有

h(18)=18 % 7=4 h(75)=75 % 7=5 h(60)=60 % 7=4 h(43)=43 % 7=1
h(54)=54 % 7=5 h(90)=90 % 7=6 h(46)=46 % 7=4

3. 数字分析法

特点:关键字在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较为均匀的若干位作为散列地址

适用:能预先估计出全体关键字的每一位上各种数字出现的频度。若更换了关键字,则需要重新构造新的散列函数

例如:

K1 K2 K3 K4 K5 K6 K7 K8
61317602 61326875 62739628 61343634 62706815 62774638 61381262 61394220

上述8个关键字可知,关键字从左到右的第1(6)、2(1)、3(3)、6(2)位取值比较集中,不宜作为哈希地址。剩余的第4、5、7、8位取值较均匀,可选取其中的两位作为哈希地址。设选取最后两位作为哈希地址,则这8个关键字的哈希地址分别为:2,75,28,34,15,38,62,20。

4. 平方取中法

特点:取关键字平方后的中间几位为哈希地址。

适用:关键字中的每一位都有某些数字重复出现频度很高的现象,这种散列地址分布比较均匀

例如:

若设哈希表长为1000则可取关键字平方值的中间三位,如图所示:

关键字 关键字的平方 哈希函数值
1234 1522756 227
2143 4592449 924
4132 17073424 734
3214 10329796 297

5. 折叠法

特点:将关键字分割成位数相同的几部分,然后取这几部分的叠加和作为散列地址

适用:关键字的数字位数特别多

例如:

当哈希表长为1000时,关键字key=110108331119891,允许的地址空间为三位十进制数,则这两种叠加情况如下:

移位叠加 边界叠加
8 9 1 8 9 1 正读
1 1 9 9 1 1 逆读
3 3 1 3 3 1 正读
1 0 8 8 0 1 逆读
+ 1 1 0 + 1 1 0 正读
(1) 5 5 9 (3) 0 4 4

处理冲突的方法

1. 开放定址法

Hi=(H(key)+di)%m(i=1,2,……,k,m=散列表表长,di为增量序列)H_i = (H(key) + d_i) \%m(i=1,2,……,k,m=散列表表长,d_i为增量序列) Hi​=(H(key)+di​)%m(i=1,2,……,k,m=散列表表长,di​为增量序列)

1. 线性探测再散列

di=1,2,3,…,m−1d_i=1, 2, 3,…,m-1di​=1,2,3,…,m−1

这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

2. 平方探测法

$ d_i=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )$

这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

3. 再散列法

这种方法是同时构造多个不同的哈希函数:

$ H_i=(H(key)+i*Hash_2(key))%m (i=1,2,…,k)$

这种方法的特点是:当哈希地址H1=H0(key)H_1=H_0(key)H1​=H0​(key)发生冲突时,再计算H2=H1(key)H_2=H_1(key)H2​=H1​(key),…,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

4. 伪随机序列法

di=伪随机序列d_i=伪随机序列di​=伪随机序列 时

2.拉链法

首先对关键字集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

代码:

struct ListNode{int val;ListNode *next;ListNode(int x): val(x), next(NULL){}
};// 哈希取余
int hash_func(int key, int table_len) {return key % table_len;
} // 使用头插法插入
void insert(ListNode *hash_table[], ListNode *node, int table_len) {int hash_key = hash_func(node->val, table_len);node->next = hash_table[hash_key];hash_table[hash_key] = node;
} // 查找
bool search(ListNode *hash_table[], int value, int table_len) {int hash_key = hash_func(node->val, table_len);ListNode *head = hash_table[hash_key];while(head) {if(head->val == value) return true;head = head->next;}return false;
}

散列查找及性能分析

查找成功公式
ALS成功=∑(pi)m(pi=每个取余关键字查找到目标值的查找次数,m取余值的大小)ALS_{成功}= \frac{\sum(p_i)}{m}(p_i=每个取余关键字查找到目标值的查找次数,m取余值的大小) ALS成功​=m∑(pi​)​(pi​=每个取余关键字查找到目标值的查找次数,m取余值的大小)
查找失败公式
ALS失败=∑(qi)m=(qi=每个取余关键字查找散列表为空或者回到源rest的查找次数,m=取余值的大小)ALS_{失败}= \frac{\sum(q_i)}{m}=(q_i=每个取余关键字查找散列表为空或者回到源rest的查找次数,m=取余值的大小) ALS失败​=m∑(qi​)​=(qi​=每个取余关键字查找散列表为空或者回到源rest的查找次数,m=取余值的大小)
示例:

关键字序列:{ 7, 8, 30, 11, 18, 9, 14}

散列函数:(key∗3)%7(key*3)\%7(key∗3)%7

key序列:{0, 3, 6, 5, 5, 6, 0}

0 1 2 3 4 5 6 7 8 9
7 14 8 11 30 18 9

查找成功的情况:

关键字 7 8 30 11 18 9 14
比较次数 1 1 1 1 3 3 2

过程:(在hash表中查找,在关键字处记录比较了多少次)

  1. 如果需要找到7,需要找pos=0,直接找到,比较1次;

  2. 如果需要找到8,需要找pos=3,直接找到,比较1次;

  3. 如果需要找到30,需要找pos=6,直接找到,比较1次;

  4. 如果需要找到11,需要找pos=5,直接找到,比较1次;

  5. 如果需要找到18,需要找pos=5,但发现hash[5] != 18,向后找;发现hash[6] != 18,继续向后找;在pos=7时找到,比较3次;

  6. 如果需要找到9,需要找pos=6,但发现hash[6] != 9,向后找;发现hash[7] != 9,继续向后找;在pos=8找到,比较3次;

  7. 如果需要找到14,需要找pos=0,但发现hash[0] != 14,向后找;在pos=1找到,比较2次;

套用公式,得

ALS成功=1+1+1+1+3+3+27=127ALS_{成功}= \frac{1+1+1+1+3+3+2}{7}=\frac{12}{7}ALS成功​=71+1+1+1+3+3+2​=712​

查找失败的情况:

取余 0 1 2 3 4 5 6
比较次数 3 2 1 2 1 5 4

过程:(依次假设key%7=rest,在hash表中查找,找到hash表中 为空 或者 回到原来的rest 处,比较次数记录到rest列表中)

  1. 如果需要找到21(63%7=0),需要找pos=0,但发现hash[0] != 21,向后找;发现hash[1] != 21,继续向后找;在pos=2为空,比较3次;
  2. 如果需要找到5(15%7=1),需要找pos=1,但发现hash[1] != 5,向后找;在pos=2为空,比较2次;
  3. 如果需要找到3(9%7=2),需要找pos=2为空,比较1次;
  4. 如果需要找到17(51%7=3),需要找pos=3,但发现hash[3] != 17,向后找;在pos=4为空,比较2次;
  5. 如果需要找到6(18%7=4),需要找pos=4为空,比较1次;
  6. 如果需要找到25(75%7=5),需要找pos=5,但发现hash[5] != 25,向后找;发现hash[6] != 25,继续向后找;发现hash[7] != 25,继续向后找;发现hash[8] != 25,继续向后找;在pos=9为空,比较5次;
  7. 如果需要找到16(48%7=6),需要找pos=6,但发现hash[,6] != 16,向后找;发现hash[7] != 16,继续向后找;发现hash[8] != 16,继续向后找;在pos=9为空,比较4次;

套用公式,得

ALS失败=3+2+1+2+1+5+47=187ALS_{失败}= \frac{3+2+1+2+1+5+4}{7}=\frac{18}{7}ALS失败​=73+2+1+2+1+5+4​=718​

【数据结构-查找】3.散列表详解相关推荐

  1. JavaScript数据结构与算法——列表详解(上)

    列表是一组有序的数据,每个数组中的数据项称为元素.数组相关知识不够了解的伙伴可以阅读本人上篇博客在JavaScript中,列表的元素可以是任意数据类型.列表中可以保存不定数量的元素,实际使用时元素的数 ...

  2. JavaScript数据结构与算法——列表详解(下),基于Nodejs实现一个列表应用

    1.上篇回顾: 上篇我们实现了一个列表类,并添加了一些属性,实现了比较多的方法,本文章将与大家一起使用列表实现一个图书借阅查询系统.需要使用JavaScript数据结构与算法--列表详解(上)中写好的 ...

  3. 高速缓存的数据结构:拉链散列表

    高速缓存的底层数据结构:拉链散列表,很多bucket,挂了很多cache entry(tag + cache line + flag). cache line就是缓存的数据,包含多个变量的值 tag指 ...

  4. 查找 之 散列表查找(哈希表)

    基础概念 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key).这里对应关系f称为散列函数,又称为哈希(Hash)函数. 采用散列技术将 ...

  5. python bisect_Python实现二分查找与bisect模块详解

    前言 其实Python 的列表(list)内部实现是一个数组,也就是一个线性表.在列表中查找元素可以使用 list.index()方法,其时间复杂度为O(n) .对于大数据量,则可以用二分查找进行优化 ...

  6. 数据结构殷人昆电子版百度云资源_数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)...

    导语 内容提要 殷人昆编著的<数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)>是清华大学出版社出版的<数据结构(C语言版)>(第2版)的配套教材,对" ...

  7. java二分查找法_java算法之二分查找法的实例详解

    java算法之二分查找法的实例详解 原理 假定查找范围为一个有序数组(如升序排列),要从中查找某一元素,如果该元素在此数组中,则返回其索引,否则返回-1.通过数组长度可取出中间位置元素的索引,将其值与 ...

  8. linux shell 字符串操作(长度,查找,替换)详解 BASH

    linux shell 字符串操作(长度,查找,替换)详解 在做shell批处理程序时候,经常会涉及到字符串相关操作.有很多命令语句,如:awk,sed都可以做字符串各种操作. 其实shell内置一系 ...

  9. 数据结构与算法之时间复杂度详解

    数据结构与算法之时间复杂度详解 目录 排序算法的介绍和分类 算法的时间复杂度概念 常见的时间复杂度解析 平均时间复杂度和最坏时间复杂度 空间复杂度介绍 1. 排序算法的介绍和分类 排序算法的介绍 排序 ...

最新文章

  1. java im 框架_Netty实战:设计一个IM框架
  2. 利用循环,使得10 * 10的二维数组具有以下值,并按以下结构输出在屏幕上
  3. Bitcoin.com支持BCH第N弹——派息神器SLP Dividend Calculator
  4. GraphQL 入门第一篇
  5. mysql数据首次导入hive_sqoop1.4.7环境搭建及mysql数据导入导出到hive
  6. vue中Router的封装以及使用
  7. 第3章 Python 数字图像处理(DIP) - 灰度变换与空间滤波13 - 平滑低通滤波器 -盒式滤波器核
  8. 存储设备分区,格式化,挂载
  9. .net framework摘抄与理解
  10. android手机可以设置屏幕锁定,安卓手机屏幕锁设置方法(九个点图案)
  11. 软件设计师的成长之路
  12. mysql insert锁 innodb_mysql – 处理ON INSERT触发器时如何锁定innodb表?
  13. Caffe编写Python layer
  14. asp.net获取浏览器的唯一标识_vue单页面应用如何在微信浏览器里进行网页授权获取用户信息
  15. 任务方案思考:句子相似度和匹配
  16. mysql配置ip和端口_连接(Connectivity)选项中,“TCP/IP”复选项选中表示启用TCP/IP网络协议,配置连接MySQL服务器的默认端口号为“3306”。_学小易找答案...
  17. 使用Intellij Idea生成可执行文件jar,转为exe文件步骤
  18. 旅行商问题(TSP) 中国34个城市 经纬度平面坐标
  19. 基于QT实现的数独游戏DPLL的SAT求解器设计
  20. 黑白简约个人网页制作 大学生个人网页设计模板 学生个人博客网页成品 简单个人网站作品下载 静态HTML CSS个人网页作业源代码

热门文章

  1. android除去标题栏或全屏
  2. Delphi资源文件的应用(转)
  3. Asp.Net 2.0中的客户端脚本
  4. 深入理解l内核v4l2框架之video for linux 2(一)
  5. LeetCode5377. 将二进制表示减到1的步骤数
  6. 练习2: Python基本图形绘制 (第2周)
  7. c语言最小元素下标怎么看,查找最小的k个元素 (C语言代码)
  8. c++新特性11 (10)shared_ptr一”概述“
  9. C++ Primer 5th笔记(chap 19 特殊工具与技术)异常类层次
  10. 多索引表 (5)创建多索引表