LeetCode 706. Design HashMap
不使用任何内建的哈希表库设计一个哈希映射(HashMap)。
实现 MyHashMap 类:
MyHashMap()用空映射初始化对象void put(int key, int value)向 HashMap 插入一个键值对(key, value)。如果key已经存在于映射中,则更新其对应的值value。int get(int key)返回特定的key所映射的value;如果映射中不包含key的映射,返回-1。void remove(key)如果映射中存在key的映射,则移除key和它所对应的value。
示例:
输入:
["MyHashMap", "put", "put", "get", "get", "put", "get", "remove", "get"]
[[], [1, 1], [2, 2], [1], [3], [2, 1], [2], [2], [2]]
输出:
[null, null, null, 1, -1, null, 1, null, -1]
解释:
MyHashMap myHashMap = new MyHashMap();
myHashMap.put(1, 1); // myHashMap 现在为 [[1,1]]
myHashMap.put(2, 2); // myHashMap 现在为 [[1,1], [2,2]]
myHashMap.get(1); // 返回 1 ,myHashMap 现在为 [[1,1], [2,2]]
myHashMap.get(3); // 返回 -1(未找到),myHashMap 现在为 [[1,1], [2,2]]
myHashMap.put(2, 1); // myHashMap 现在为 [[1,1], [2,1]](更新已有的值)
myHashMap.get(2); // 返回 1 ,myHashMap 现在为 [[1,1], [2,1]]
myHashMap.remove(2); // 删除键为 2 的数据,myHashMap 现在为 [[1,1]]
myHashMap.get(2); // 返回 -1(未找到),myHashMap 现在为 [[1,1]]
提示:
0 <= key, value <= 106- 最多调用
104次put、get和remove方法
算法1
(拉链法) O(1)
哈希表的基本思想都是先开一个大数组,然后用某种哈希函数将key映射到数组的下标空间。不同算法的区别在于如何处理下标冲突,即当两个不同的key被映射到同一下标时,该怎么办。\
一般有两种方式处理冲突:拉链法和开放寻址法。 首先我们来介绍拉链法。它的思想很简单,在哈希表中的每个位置上,用一个链表来存储所有映射到该位置的元素。
对于put(key, value)操作,我们先求出key的哈希值,然后遍历该位置上的链表,如果链表中包含key,则更新其对应的value;如果链表中不包含key,则直接将(key,value)插入该链表中。
对于get(key)操作,求出key对应的哈希值后,遍历该位置上的链表,如果key在链表中,则返回其对应的value,否则返回-1。
对于remove(key),求出key的哈希值后,遍历该位置上的链表,如果key在链表中,则将其删除。
时间复杂度:
最坏情况下,所有key的哈希值都相同,且key互不相同,则所有操作的时间复杂度都是 O(n)。但最坏情况很难达到,每个操作的期望时间复杂度是 O(1)。
空间复杂度:
一般情况下,初始的大数组开到总数据量的两到三倍大小即可,且所有链表的总长度是 O(n) 级别的,所以总空间复杂度是 O(n)。
C++ 代码
class MyHashMap {
public:
/** Initialize your data structure here. */
const static int N = 20011;
vector<list<pair<int,int>>> hash;
MyHashMap() {
hash = vector<list<pair<int,int>>>(N);
}
list<pair<int,int>>::iterator find(int key)
{
int t = key % N;
auto it = hash[t].begin();
for (; it != hash[t].end(); it ++ )
if (it->first == key)
break;
return it;
}
/** value will always be non-negative. */
void put(int key, int value) {
int t = key % N;
auto it = find(key);
if (it == hash[t].end())
hash[t].push_back(make_pair(key, value));
else
it->second = value;
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
int get(int key) {
auto it = find(key);
if (it == hash[key % N].end())
return -1;
return it->second;
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
void remove(int key) {
int t = key % N;
auto it = find(key);
if (it != hash[t].end())
hash[t].erase(it);
}
};
/**
* Your MyHashMap object will be instantiated and called as such:
* MyHashMap obj = new MyHashMap();
* obj.put(key,value);
* int param_2 = obj.get(key);
* obj.remove(key);
*/
算法2
(开放寻址法) O(1)
开放寻址法的基本思想是这样的:如果当前位置已经被占,则顺次查看下一个位置,直到找到一个空位置为止。
对于put(key, value)操作,求出key的哈希值后,顺次往后找,直到找到key或者找到一个空位置为止,然后将key放到该位置上,同时更新相应的value。
对于get(key)操作,求出key的哈希值后,顺次往后找,直到找到key或者-1为止(注意空位置有两种:-1和-2,这里找到-1才会停止),如果找到了key,则返回对应的value。
对于remove(key)操作,求出key的哈希值后,顺次往后找,直到找到key或者-1为止,如果找到了key,则将该位置的key改为-2,表示该数已被删除。
注意:当我们把一个key删除后,不能将其改成-1,而应该打上另一种标记。否则一个连续的链会从该位置断开,导致后面的数查询不到。
时间复杂度:
最坏情况下,所有key的哈希值都相同,且key互不相同,则所有操作的时间复杂度都是 O(n)。但实际应用中最坏情况难以遇到,每种操作的期望时间复杂度是 O(1)。
空间复杂度:
一般来说,初始大数组开到总数据量的两到三倍,就可以得到比较好的运行效率,空间复杂度是 O(n)。
C++ 代码
class MyHashMap {
public:
/** Initialize your data structure here. */
const static int N = 20011;
int hash_key[N], hash_value[N];
MyHashMap() {
memset(hash_key, -1, sizeof hash_key);
}
int find(int key)
{
int t = key % N;
while (hash_key[t] != key && hash_key[t] != -1)
{
if ( ++t == N) t = 0;
}
return t;
}
/** value will always be non-negative. */
void put(int key, int value) {
int t = find(key);
hash_key[t] = key;
hash_value[t] = value;
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
int get(int key) {
int t = find(key);
if (hash_key[t] == -1) return -1;
return hash_value[t];
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
void remove(int key) {
int t = find(key);
if (hash_key[t] != -1)
hash_key[t] = -2;
}
};
/**
* Your MyHashMap object will be instantiated and called as such:
* MyHashMap obj = new MyHashMap();
* obj.put(key,value);
* int param_2 = obj.get(key);
* obj.remove(key);
*/