数据结构四——散列表(上)

文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。

1散列表的由来

从数组随机访问特性说起。
 数组的随机访问特性是:数组a,a[5]可以直接访问到数组的第6个元素。这就类似于在下标和数组对应的值之间建立了映射关系。
 散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。没有数组就没有散列表。
 应用在实践中。例如有100名学生,每个学生有一个学号,学号是从0到99,数组score存放每个学生的成绩,score[0]表示学号是0的学生的成绩,score[1]表示学号是1的学生的成绩… 学号->数组下标->学生成绩。f1(学号)=数组下标。
 现在更进一步,学号的规则是“年级+班级+数字”,例如“50137”表示5年级1班,37还是和上面的含义一样。那么我们就可以写函数把学号和数组下标映射起来,查找学生成绩。依然是:学号->数组下标->学生成绩。f2(学号)=数组下标。f1与f2不同。
 这里的学号就是key,学生成绩是value,f1、f2是散列函数。散列函数计算得到的值是哈希值。

2散列函数

散列函数一般表示为hash(key)
 散列函数的三点要求。
 1 散列值是非负的。
 2 如果key1=key2,那么hash(key1)=hash(key2)。
 3 如果key1̸=key2key1\not= key2key1̸=key2,那么hash(key1)̸=hash(key2)hash(key1)\not=hash(key2)hash(key1)̸=hash(key2)

3散列冲突

定义
 在实际中因为数组存储空间的限制,要想做到key值不同的时候哈希值不同,几乎很难满足。这个时候就产生了散列冲突,也要哈希冲突。换句话说就是数组只有10个下标,学生有5个人,但学号是随机的,怎么映射,能够快速访问到学生成绩。

3.1开放寻址法

开放寻址法=可以改变哈希值的解决方法
 开放寻址法的核心思想是:如果发生了散列冲突,就重新找一个空闲位置插入数据。怎么找呢?三种方法:线性探测、平方探测、再哈希。
 在这里插入图片描述

线性探测

如果hash(key)=7,且数组score[7]已经被占用,那就探测8的位置是不是被占用,9的位置是不是被占用,0的位置是不是被占用…一直找到空闲位置。探测顺序是(hash(key)+0)%size,(hash(key)+1)%size,(hash(key)+2)%size,(hash(key)+3)%size…

平方探测

如果hash(key)=7,且数组score[7]已经被占用,那就探测8的位置是不是被占用,1的位置是不是被占用,8的位置是不是被占用…一直找到空闲位置。探测顺序是(hash(key)+0)%size,(hash(key)+1)%size,(hash(key)+4)%size,(hash(key)+9)%size…
 数组大小是有限的,再探测的时候一定要对数组大小size取余。

再哈希

再哈希是指当发生冲突的时候,再找一个散列函数计算,探测空间是不是被占用,如果被占用,继续再找一个散列函数计算。
 线性探测和平方探测其实是再哈希的特殊形式。再哈希的函数f(x)=x,或者 f(x)=x2f(x)=x^2f(x)=x2

查找

在这里插入图片描述
 查找过程和插入过程类似。我们通过散列函数求出要查找元素key值的哈希值,然后比较数组中下标为散列值的key和要查找的元素key值。如果相等则查找到,否则继续探测查找,直到数组中出现空闲位置。
 我的思考:散列表中存储的是value值。例如最上面的例子,学号就是数组下标的时候,散列表就是数组,存储的是学生成绩。当学号变成随机的时候,散列表中存储的是学生实体。包含学号和学生成绩。查找的时候 比对的是key值是否相同。学号->哈希值->学生成绩。

删除操作

当需要删除数据的时候,需要注意不能直接将数组的值置为空。因为在查找过程中出现空闲位置就停止不找了。这样查找就不准确了。可以将该位置放置删除标记。

3.2 链表法

在这里插入图片描述

所有哈希值相同的元素放在同一个槽(slot)或者链表内,形成一个链表。
 当插入的时候只需要计算插入元素key值的哈希值,找到对应的slot,添加到链表中即可。时间复杂度O(1)。
 当查找或者删除的时候同样计算哈希值,添加或者删除链表中的元素。时间复杂度与链表的长度k成正比,所以是O(k)。所以更希望哈希值的分布式均匀的。

4散列函数与内存

4.1 散列函数的设计要求

散列函数需要满足:
1 设计简单高效,计算时间短。
2 生成的值要随机且均匀。

数据分析法设计散列函数。例如学号复杂的例子,我们分析学号的特征设计散列函数。
 key为字符串类型的可以使用字符串进位相加的方法,然后再跟散列表大小取余。例如"nice"的哈希值为:

hash("nice")=(("n" - "a") * 26*26*26 + ("i" - "a")*26*26 + ("c" - "a")*26+ ("e"-"a")) / 78978

还有其他设计函数的方法。例如:直接寻址法、平方取中法、随机数法等。

4.2动态装载因子

散列表中元素个数m与散列表长度的比值就是装在因子:

装载因子=元素个数长度装载因子=\dfrac{元素个数}{长度}=
 散列表中随着数据的插入和删除状态因子发生变化,成为动态装载因子。

4.3 扩容、缩容

当加载因子不断变大的时候,发生散列冲突的概率就会增加,操作就会变慢。这时候可以像动态数组一样做扩容。
 一般散列表扩容会在在当前长度的基础上再扩一倍。扩容之前装载因子是0.8,扩容之后就是0.4。散列表扩容与数组扩容不同的地方是扩容之后,因为散列表大小发生变化,散列值也可能发生变化。例如原来key=6的元素,哈希值是1,扩容后哈希值是10。
 支持动态扩容散列表的插入操作的平均时间复杂度,按照摊还分析法是O(1)。
 当散列表随着删除操作,装载因子会越来越小。如果对内存不敏感,浪费一些也可以,可以不采取操作。如果要求内存尽可能小,可以对散列表缩容。
 装载因子需要权衡时间和空间。操作时间优先,可以允许浪费一定的空间。

4.4避免低效扩容

低效扩容是因为一次扩容,重新计算哈希值,搬移数据导致的。如果原来的数据有1G大小,这一次搬移操作就很费劲。
 我们可以采集的策略是将原始n个数据的搬移工作分配到n次插入操作中。每次插入只将原来散列表中的一个值搬移到新散列表。这样在任何时候插入操作的时间复杂度都是O(1)。
 在这里插入图片描述

5 如何选择冲突解决方法

开放寻址法
 优点:底层结构是数组,可以充分利用CPU缓存加快查询速度;利于序列化。
 缺点:删除数据需要标记,比较麻烦;所有数据在同一个数组,冲突的代价更大,所以会浪费更多的内存空间。
 当数据量比较小、装载因子比较小的时候,选择开放寻址法。例如Java的ThreadLocalMap。

链表法
 优点:内存利用率比开放寻址法高,因为节点可以在需要的时候才创建而不是提前创建。
 可以容忍装载因子大于1。当装载因子大于1,查找速度与每个槽对应的链表长度有关,但是比全链表查询效果要高。我们可以将链表改为跳表或者红黑树,这样即便出现散列冲突的极端情况,时间复杂度也是O(logn)。
 当数据对象比较大、数据量比较大的时候使用链表法。

6工业级散列表举例分析

6.1 Java的HashMap

1 初始大小16,可以指定。
2 装载因子0.75,当装载因子大于0.75的时候动态扩容。
3 冲突解决方法:链表法。当链表长度超过8,使用红黑树。
4 hash函数:

int hash(Object key) {int h = key.hashCode();return (h ^ (h >>> 16)) & (capitity -1); //capicity 表示散列表的大小
}

hashcode返回的是key的hash code。

6.2 工业级散列表应该具有的特征

1 支持快速查询、插入、删除;
2 内存占用合理;
3 性能稳定,在极端情况下也不会出现速度不可接受的情况。

6.3 工业级散列表设计思路

1 散列函数设计合适。
2 装载因子设置合理,不过多浪费空间,动态扩容策略合适。
2 散列冲突解决策略。

7散列表的应用

7.1 word中错误单词提示功能

把英文单词加载到内存中,用散列表存储。当用户输入一个词的时候,在散列表中查找是否存在,
常用因为单词20万,假设单词平均长度10个字母。一个单词占用10个字节的内存,所有单词加载大约是2M内存。放大10倍20M。内存可用。

7.2 假设有10条url访问记录,怎么按照访问次数给url排序

对url取哈希值,在散列表存储每个hash值的访问次数。最后再排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/424609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[BUGKU][CTF][Reverse][2020] Reverse writeup 1-7 暂时肝不动了

Reverse 入门逆向 步骤: ida main函数 按R Reverse signin 关键字: 知识点:Android逆向分析。(常用工具:安卓模拟器、JEB、Cyberchef、Androidkiller) 步骤: 1.用jeb打开,找到MainActivity,右…

spring学习(4):spring管理对象之间的关联关系

接着上一节的课程继续学习 MesasageService 类 package hello;import org.springframework.stereotype.Component; //注解的加入 Component public class MesasageService {public MesasageService() {super();System.out.println("MessageService...");}/*** 执行打…

[Lua]LuaAPI整理

ref :https://blog.csdn.net/ouyangshima/article/details/43339571 LUA和C/C的沟通桥梁——栈 Lua生来就是为了和C交互的,因此使用C扩展Lua或者将Lua嵌入到C当中都是非常流行的做法。要想理解C和Lua的交互方式,首先要回顾一下C语言是如何处理…

[hackinglab][CTF][基础关][2020] hackinglab 基础关 writeup

在线工具:https://www.qqxiuzi.cn/daohang.htm 基础关 1 key在哪里? 知识点:F12查看源代码 步骤:F12 基础关 2 再加密一次你就得到key啦~ 知识点:ROT13 步骤: 基础关 3 猜猜这是经过了多少次加密? 关键字&#xf…

spring学习(5):spring简介

1什么是spring 核心概念 spring框架组成

算法四——哈希

文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。 哈希算法的定义 文章来自极客时间。 参考网页 定义:将任意长度的二进制值串映射为固定长度的二进制值串。映射之后的二进制值串称为哈希值。 符合几点要…

spring学习(6):使用xml方式实现spring基本应用

接着上一节的课程继续学习 在resources文件下建立applicationconText applicationconText.xml文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3…

[burp][CTF]burp intruder爆破出现 Payload set 1: Invalid number settings的解决办法

这是一个软件bug 如果点击start attrack 后出现 Payload set 1: Invalid number settings 的提示&#xff0c;先点hex 后点 decimal 再开始start attrack&#xff0c;需要手动让它刷新。

数据结构五——二叉树

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 树 1.1 概念 概念&#xff1a;树、根、父节点、子节点、叶子节点。 几个度&#xff1a;高度、深度、层。与实际生活中的这几个概念类比。 高度&#xff1a;从下往…

spring学习(7):加入log4g日志系统

点击show dependencies popup 修改pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&quo…

[hackinglab][CTF][脚本关][2020] hackinglab 脚本关 writeup

脚本关 1 key又又找不到了 关键字&#xff1a; 知识点&#xff1a; 步骤:点击提供的链接后&#xff0c;实际发生了两次跳转&#xff0c;key 在第一次跳转的网页中&#xff0c;key is : yougotit_script_now 脚本关 2 快速口算 关键字&#xff1a; 知识点&#xff1a;python基…

用递归树求解递归算法时间复杂度

文章内容、图片均来自极客时间。 递归代码复杂度分析起来比较麻烦。一般来说有两种分析方法&#xff1a;递推公式和递归树。 1 递推公式法 归并排序的递推公式是&#xff1a; merge_sort(p…r) merge(merge_sort(p…q), merge_sort(q1…r)) 终止条件&#xff1a; p > r …

spring学习(8):log4j.properties 详解与配置步骤

一、入门实例 1.新建一个JAva工程&#xff0c;导入包log4j-1.2.17.jar&#xff0c;整个工程最终目录如下 2、src同级创建并设置log4j.properties ### 设置### log4j.rootLogger debug,stdout,D,E### 输出信息到控制抬 ### log4j.appender.stdout org.apache.log4j.ConsoleAp…

[hackinglab][CTF][注入关][2020] hackinglab 注入关 writeup

服务器挂了 咕咕咕 参考链接&#xff1a;https://blog.csdn.net/weixin_41924764/article/details/107095963?utm_mediumdistribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_sourcedistribute.pc_relevant_t0.none-task-bl…

数据结构六——堆

文章出处&#xff1a;极客时间《数据结构和算法之美》-作者&#xff1a;王争。该系列文章是本人的学习笔记。 1 堆定义 1.1 定义和结构 堆是一个完全二叉树&#xff08;完全二叉树&#xff1a;除了叶子节点外每一层节点都是满的&#xff0c;最后一层的子节点都靠左排列&…

spring学习(9):idea的config配置

点开eventlog 点击之后 勾选 apply---ok

[hackinglab][CTF][上传关][2020] hackinglab 上传关 writeup

上传关 1 请上传一张jpg格式的图片 关键字&#xff1a; 步骤&#xff1a; 1.F12查看源码 2.输入网址 获得key http://lab1.xseclab.com/upload1_a4daf6890f1166fd88f386f098b182af/upload_file.php上传关 2 关键字&#xff1a;burp 知识点&#xff1a; 步骤&#xff1a;看源…

靠谱的Pycharm安装详细教程

如何在本机上下载和安装Pycharm&#xff0c;具体的教程如下&#xff1a; 1、首先去Pycharm官网&#xff0c;或者直接输入网址&#xff1a;http://www.jetbrains.com/pycharm/download/#sectionwindows&#xff0c;下载PyCharm安装包&#xff0c;根据自己电脑的操作系统进行选择…

第四十四期:Keep突然大裁员,回应称“属正常调整”!

10 月 24 日程序员节原本是程序员们最开心的一天&#xff0c;但 Keep 程序员却开心不起来&#xff0c;因为不少人突然面临失业危机。\ 作者&#xff1a;51CTO 10 月 24 日程序员节原本是程序员们最开心的一天&#xff0c;但 Keep 程序员却开心不起来&#xff0c;因为不少人突然…

第四十五期:万亿级日访问量下,Redis在微博的9年优化历程

Redis在微博内部分布在各个应用场景&#xff0c;比如像现在春晚必争的“红包飞”活动&#xff0c;还有像粉丝数、用户数、阅读数、转评赞、评论盖楼、广告推荐、负反馈、音乐榜单等等都有用到Redis。 作者&#xff1a;兰将州来源 一、Redis在微博的应用场景 Redis在微博内部…