平时我们在编程的时候,经常会用到哈希表处理和储存数据,那么如果让你实现一个简单的哈希表,你会怎么实现呢?
原题链接705. 设计哈希集合
一、先看题
不使用任何内建的哈希表库设计一个哈希集合(HashSet)。
实现 MyHashSet 类:
void add(key)向哈希集合中插入值 key 。bool contains(key)返回哈希集合中是否存在这个值 key 。void remove(key)将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
示例:
输入:
["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"]
[[], [1], [2], [1], [3], [2], [2], [2], [2]] 输出:
[null, null, null, true, false, null, true, null, false] 解释:
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1); // set = [1]
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(1); // 返回 True
myHashSet.contains(3); // 返回 False ,(未找到)
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(2); // 返回 True
myHashSet.remove(2); // set = [1]
myHashSet.contains(2); // 返回 False ,(已移除)
提示:
- 最多调用 104 次
add、remove和contains
二、整理思路
在我们实现简易哈希表的数据结构之前,我们首先要分析,对于题中给出的简易哈希表应该要有哪些功能,以及初步的实现思路。
第一步:
确认输入输出:
输入: 的数
输出: 由示例可知,只有contains方法有bool值返回,其他方法无需返回值
第二步
怎么实现这个数据结构?
作为一个哈希表,我们首先肯定要有存储数据的属性,我们将其命名为data。
除了data外,还有题干中给出的三种方法:add、contains、remove。
由于无法使用内建的哈希表库,因此我们可以采取数组来进行模拟。除了题中的三种方法,也没有其他的要求,因此我们可以采取一些优化措施,比如存储的时候将数进行分组,从而避免冲突。
在这里我们使用整数除法来作为哈希函数。为了尽量避免冲突,我们将除数BASE定为一个质数。
因此,得到了下面的算法:
/**
* Initialize your data structure here.
*/
var MyHashSet = function () {
this.BASE = 971;
this.data = new Array(this.BASE).fill(0).map(() => new Array())
};
/**
* @param {number} key
* @return {void}
*/
MyHashSet.prototype.add = function (key) {
const h = key % this.BASE
for (let ele of this.data[h]) {
if (ele === key) return
}
this.data[h].push(key)
};
/**
* @param {number} key
* @return {void}
*/
MyHashSet.prototype.remove = function (key) {
const h = key % this.BASE
for (let i = 0; i < this.data[h].length; i++) {
if (this.data[h][i] === key) {
this.data[h].splice(i, 1)
return
}
}
};
/**
* Returns true if this set contains the specified element
* @param {number} key
* @return {boolean}
*/
MyHashSet.prototype.contains = function (key) {
const h = key % this.BASE
return this.data[h].includes(key)
};
/**
* Your MyHashSet object will be instantiated and called as such:
* var obj = new MyHashSet()
* obj.add(key)
* obj.remove(key)
* var param_3 = obj.contains(key)
*/
对于为什么质数取模,采用leetcoder@果然翁的原话就是:
质数取模,其实是利用了同余的感念:当元素是个有规律的等差数列时,并且和基数(数组大小)最大公约数不为1时,就会造成哈希映射时冲突变高(数组某些位置永远不会有值)。
比如数列0,6,12,18,24..., base为10,取模放入哈希表中位置将只能在0,2,4,6,8这几个数组位置上
但我们如果把base取7(数组大小甚至比10小),同样数列可以分布在哈希表中的0,1,2,3,4,5,6
可以使得哈希表中每个位置都“有用武之地”
三、总结
知识点:基础数据结构实现
解题法:
对于平时用习惯的基础数据结构,我们也应该适当深入了解下实现原理,这样才能更好地使用这些工具,最大化他们的使用价值。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情