hashmap赋值给另一个hashmap_图解设计一个 HashMap
目前我们学到的数据结构有:单链表,双向链表,栈,队列,循环队列,双端队列。今天学习 LeetCode 的 「 706. Design HashMap 」,从设计一个 HashMap 到掌握其内部原理。题目要求:
Design a HashMap without using any built-in hash table libraries.
To be specific, your design should include these functions:put(key, value) : Insert a (key, value) pair into the HashMap. If the value already exists in the HashMap, update the value.get(key): Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key.remove(key) : Remove the mapping for the value key if this map contains the mapping for the key.
All keys and values will be in the range of [0, 1000000]
.
The number of operations will be in the range of [1, 10000]
.
题目要求设计一个 HashMap,不能使用语言提供的类似哈希表的库,比如HashMap,dict,map,需要实现下面几个方法:
1. put(key, value) : 根据 key 插入一个 value,如果 key 已经存在,更新 value;
2. get(key): 根据一个 key 获取对应的值,如果未找到对应的 value,返回 -1;
3. remove(key) : 根据 key 来删除对应的值。
分析
HashMap 是一种典型的以空间换取时间的数据结构,在设计缓存算法 LRU 和 LFU 当中用到了 C++ 提供的 unorder_map,利用HashMap 的特点,做到存取时间复杂度为 O(1) 。今天我们要掌握如何设计一个 HashMap。设计之前需要知道 HashMap 是一种什么样的数据结构?
HashMap 的核心思想是 「 把 key 通过一种方式转换成一个 hashCode(一个整形数),通过 hashCode 来存取对应的 value 」,转换的方式就是哈希函数,在转换的过程中,不同的 key 可能会生成同一 hashCode,这将产生「哈希冲突」。
一图胜千言!
插入 key 对应的 value 函数为:put(key, value):
执行 put(1, 1),1 的 hash 值 hash(1) = 1 % 5 = 1,放到 1 个位置;
执行 put(4, 4),4 的 hash 值 hash(4) = 4 % 5 = 4,放到 4 个位置;
执行 put(6, 6),6 的 hash 值 hash(6) = 6 % 5 = 1,放到 1 个位置,第一个位置已经存放了 1,产生「哈希冲突」;
综上,设计一个哈希表需要做下面 2 件事:
1.设计哈希函数;
衡量一个哈希函数设计的好坏是看它是否能够让 value 「均匀分布」,也就是产生哈希冲突越少越好。语言本身一般会提供计算 hashCode 的方法,比如 OC 中的 NSObject 类提供了 hash 方法:
NSString *name = @"Lefe_x";NSString *des = @"超越技术公众号做图解算法";NSLog(@"hash(name) = %@, hash(des)=%@", @(name.hash), @(des.hash));// hash(name) = 7306077673678745, hash(des)=7723704617483326955
2.解决哈希冲突;
不同的 key 生成的 hashCode 相同就产生了哈希冲突,解决冲突有主要有下面几种方式:
链地址法:产生哈希冲突后,把产生冲突的元素使用某种方式「组合」到一起,可以使用链表、红黑树,或者使用其它数据结构。
把 1、6、3、4、13 分别 put 到哈希表中,1、6、13 的哈希值均为 1,被放到第一个位置,可以通过链表、红黑树进行存储。
开地址法:产生哈希冲突后,把 value 放到其它空闲位置,可以使用线性探测法放到下一个空闲位置;使用平方探测法,放到第1个、第 4个、第9个、第16个......空闲位置;使用二次哈希,通过另外一个哈希函数再计算一次哈希值。
使用线性探测法解决冲突,把 1、6、3、4、13 分别 put 到哈希表中,1、6、13 的哈希值均为 1,会产生冲突,当遇到冲突后,把 value 插入到下一个位置。保存结果如下图:
代码
通过上面的分析可知,实现一个 HashMap,需要一个实现一个哈希函数和解决哈希冲突,代码中通过「链地址法」来解决哈希冲突。题目中的 key 和 value 的取值范围为 [ 0 - 1000000 ]。代码原理如图:
C++ 代码如下(来源于 LeetCode):
#include #include #include using namespace std;class MyHashMap { size_t m_size = 10000; vector<listint, public: // 初始化,设置大小 MyHashMap() { m_data.resize(m_size); } // 哈希函数 int hashCode(int key) { return key % m_size; } // 根据 key 存储对应的 value,如果 key 已经存在,更新 value void put(int key, int value) { // 根据哈希函数找到对应的链表 auto &list = m_data[hashCode(key)]; for (auto & val : list) { // 如果已经存在,根据 key 来更新对应的值 if (val.first == key) { val.second = value; return; } } // 插入链表的尾部 list.emplace_back(key, value); } // 根据 key 来获取值 int get(int key) { const auto &list = m_data[hashCode(key)]; if (list.empty()) { return -1; } for (auto & val : list) { // 如果已经存在,找到了对应的值 if (val.first == key) { return val.second; } } return -1; } // 根据 key 删除对应的值 void remove(int key) { auto &list = m_data[hashCode(key)]; // 找到节点后删除 list.remove_if([key](auto n) { return n.first == key; }); }};
总结
至此,一个简单的 HashMap 就完成了,如果设计一个复杂的 HashMap,需要考虑数据达到一定程度后,需要对 vector 进行扩容、缩容处理,如果冲突达到某一个量级后,需要考虑更换 list 这个数据结构,比如换成红黑树。
推荐阅读:
论证:学习数据结构和算法很重要
图解 LFU cache
图解数据结构和算法
hashmap赋值给另一个hashmap_图解设计一个 HashMap相关推荐
- HashMap底层红黑树实现(自己实现一个简单的红黑树)
文章整理于小刘老师讲源码 视频学习链接:小刘老师讲解红黑树 JDK集合源码之HashMap解析(上) JDK集合源码之HashMap解析(下) 1.树结构入门 1.1 什么是树? 树(tree)是一种 ...
- python输入一个正整数n求下列算式的值_C语言编写程序:输入一个正整数x和一个正整数n,求下列算式的值。,C语言 编写一个程序,输入一个正整数,求出它是几位数。...
导航:网站首页 > C语言编写程序:输入一个正整数x和一个正整数n,求下列算式的值.,C语言 编写一个程序,输入一个正整数,求出它是几位数. C语言编写程序:输入一个正整数x和一个正整数n,求下 ...
- Java黑皮书课后题第4章:*4.17(一个月的天数)编写一个程序,提示用户输入一个年份和一个月份名称的前3个字母(第一个字母使用大写形式),显示该月中的天数。如果月份非法则显示出错信息
*4.17(一个月的天数)编写一个程序,提示用户输入一个年份和一个月份名称的前3个字母(第一个字母使用大写形式),显示该月中的天数.如果月份非法则显示出错信息 题目 题目概述 破题 运行示例 代码 题 ...
- 在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如RXXLRXRXL)中进行移动操作。一次移动操作指用一个LX替换一个XL,或者用一个XR替换一个RX。现给定起始...
在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作.一次移动操作指用一个"LX"替换一个"XL ...
- java 一个数组key一个数组value_在各种语言中,使用key在map中获取value 和 使用下标获取数组中的数据 相比哪个更快?...
数组和集合的效率问题数组是JAVA语言内置的数据类型,它是一个线性的序列,所以它可以快速的访问其他的元素.但是速度是要有代价的,当你创建了一个数组之后,它的容量就固定了,而且在其生命周期里是不能改变的 ...
- DOM算法系列002-寻找指定DOM节点的上一个或下一个节点
DOM操作算法002-寻找指定DOM节点的上一个或下一个节点-- getDomNode 当我们需要寻找指定DOM节点的上一个节点或下一个节点时,我们可能第一时间会想到下面两个API: node.pre ...
- 【Jquery】-------JS实现关键字检索html内容,符合关键字的匹配项,进行标注背景色,可进行上一个,下一个切换定位
JS实现关键字检索html内容,符合关键字的匹配项,进行标注背景色 核心代码 全部代码 展示效果 核心代码 全部代码 这个代码主要功能: 通过关键字检索出html内容匹配项 可进行上一个,下一个切换定 ...
- C语言中的内聚与耦合(遵循“一个函数,一个功能”的原则)
文章目录 一.原理篇 低耦合 非直接耦合与数据耦合(值传递不传指针) 特征耦合(传指针并可修改指针指向内存:不同函数打开同一文件进行操作) 外部耦合(访问同一全局变量,不通过参数表传递全局变量信息:通 ...
- C语言返回指针的函数,指针函数,让一个函数返回一个字符串
C语言函数返回指针的函数(指针函数) 什么是返回指针的函数 一个函数可以返回一个整形值 字符型值 实型值 1.如果一个函数它的返回值是一个地址(是一个指针的话),这个函数是一个返回值是指针即指针函数. ...
最新文章
- dbscan算法中 参数的意义_基于变参数的DBSCAN算法
- 【pandas学习笔记】综合整理
- 怎么自学python自动化测试-Python移动自动化测试面试 学习 教程
- C语言遥控器程序,红外遥控
- HAAR、LBP分类器训练
- nx600打印机打印设置_win7打印机共享怎么设置
- PHP优于Node.js的五大理由
- leetcode 无重复字符的最长子串
- anaconda下载jupyter写python_如何安装Anaconda3和使用Jupyter
- 【连载】如何掌握openGauss数据库核心技术?秘诀三:拿捏存储技术(3)
- Python Re 模块超全解读
- android中正则表达式截取html中的video标签
- Only the Paranoid Survive
- RxSwift极简入门
- 区块链主流开源技术体系介绍(转)
- 芯片模型算力指标TOPS FLOPS MAC MACC MADD关系
- php中文输出有乱码怎么办,php中文输出乱码怎么办
- 游戏直播平台新赛程:负重前行与危中求生
- 2021 年 Q4 随笔
- buuctf在线测评web Secret File