题目
不使用任何内建的哈希表库设计一个哈希映射
具体地说,你的设计应该包含以下的功能
put(key, value)
:向哈希映射中插入(键,值)的数值对。如果键对应的值已经存在,更新这个值。get(key)
:返回给定的键所对应的值,如果映射中不包含这个键,返回-1。remove(key)
:如果映射中存在这个键,删除这个数值对。
示例:
MyHashMap hashMap = new MyHashMap();
hashMap.put(1, 1);
hashMap.put(2, 2);
hashMap.get(1); // 返回 1
hashMap.get(3); // 返回 -1 (未找到)
hashMap.put(2, 1); // 更新已有的值
hashMap.get(2); // 返回 1
hashMap.remove(2); // 删除键为2的数据
hashMap.get(2); // 返回 -1 (未找到)
注意:
- 所有的值都在
[1, 1000000]
的范围内。 - 操作的总数目在
[1, 10000]
范围内。 - 不要使用内建的哈希库。
方案一(切片)
type MyHashMap struct {
set []bool
container []int
}
/** Initialize your data structure here. */
func Constructor() MyHashMap {
return MyHashMap {
set: make([]bool, 1000001, 1000001),
container: make([]int, 1000001, 1000001),
}
}
/** value will always be non-negative. */
func (this *MyHashMap) Put(key int, value int) {
this.set[key] = true
this.container[key] = value
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
func (this *MyHashMap) Get(key int) int {
in := this.set[key]
value := this.container[key]
if in {
return value
} else {
return -1
}
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
func (this *MyHashMap) Remove(key int) {
this.set[key] = false
}
因为值可能是0,所以直接使用一个数组来存储
Get
时会有麻烦。
方案二
参考 leetcode 方案,可使用以下方案
type hash struct {
value int
}
type MyHashMap struct {
container []*hash
}
/** Initialize your data structure here. */
func Constructor() MyHashMap {
return MyHashMap {
container: make([]*hash, 1000001, 1000001),
}
}
/** value will always be non-negative. */
func (this *MyHashMap) Put(key int, value int) {
hash := &hash {
value: value,
}
this.container[key] = hash
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
func (this *MyHashMap) Get(key int) int {
hash := this.container[key]
if hash != nil {
return hash.value
} else {
return -1
}
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
func (this *MyHashMap) Remove(key int) {
this.container[key] = nil
}
方案三
const NUMBER_OF_BUCKET = 1000
type hash struct {
key int
value int
}
type MyHashMap struct {
bucket [][]*hash
}
/** Initialize your data structure here. */
func Constructor() MyHashMap {
return MyHashMap {
bucket: make([][]*hash, NUMBER_OF_BUCKET, NUMBER_OF_BUCKET),
}
}
func (this *MyHashMap)getBucket(key int) []*hash {
index := key % NUMBER_OF_BUCKET
return this.bucket[index]
}
/** value will always be non-negative. */
func (this *MyHashMap) Put(key int, value int) {
bucket := this.getBucket(key)
for _, hash := range bucket {
if hash != nil && hash.key == key {
hash.value = value
}
}
_hash := &hash{
key: key,
value: value,
}
index := key % NUMBER_OF_BUCKET
this.bucket[index] = append(this.bucket[index], _hash)
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
func (this *MyHashMap) Get(key int) int {
bucket := this.getBucket(key)
for _, hash := range bucket {
if hash != nil && hash.key == key {
return hash.value
}
}
return -1
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
func (this *MyHashMap) Remove(key int) {
bucket := this.getBucket(key)
for i, hash := range bucket {
if hash != nil && hash.key == key {
bucket[i] = nil
}
}
}
- 该方案是性能最好的(来自 leetcode 测试);
- 其实用链表更好,因为此处没有按索引查询值的需求,而且删除操作此处直接置为nil,使用链表删除此节点会更好些。
更多
我们来看看 删除 操作。在找到元素的位置之后,需要从数组列表中删除元素。
假设我们要删除第 i
个元素,并且数组列表的大小为 n
。
内置函数中使用的策略是把第 i
个元素后的所有元素向前移动一个位置。也就是说,必须移动 n - i
次。因此,从数组列表中删除元素的时间复杂度将为 O(n)。
考虑 i
取不同值的情况。平均而言,我们将移动 ((n - 1) + (n - 2) + ... + 1 + 0) / n = (n - 1) / 2
次。
有两种解决方案可以将时间复杂度从 降低到 。
- 交换
首先,用存储桶中的最后一个元素交换要移除的元素。然后删除最后一个元素。通过这种方法,成功地在 的时间复杂度中去除了元素。
- 链表
实现此目标的另一种方法是使用链表而不是数组列表。通过这种方式,可以在不修改列表中的顺序的情况下删除元素。该策略时间复杂度为 。
原文
https://leetcode-cn.com/explore/learn/card/hash-table/203/design-a-hash-table/800/