题目:
不使用任何内建的哈希表库设计一个哈希映射(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
方法
思路:
哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上。
冲突处理:由于不同元素可能映射到相同的整数值,因此需要在整数值出现“冲突”时,进行冲突处理。以下代码使用的解决冲突处理的方法是链地址法。
链地址法:为每个哈希值维护一个链表,并将具有相同哈希值的元素都放入这一链表当中。
设哈希表的大小为HSIZE,则可以设计一个简单的哈希函数计算哈希值x,x=key%HSIZE。
我们开辟一个大小为 HSIZE的数组,数组的每个位置是一个链表。当计算出哈希值之后,就插入到对应位置的链表当中。
由于我们使用整数除法作为哈希函数,为了尽可能避免冲突,应当将HSIZE 取为一个质数。
代码:
typedef struct List
{
int key;
int val;
struct List* next;
}List;
void Insret(List* l1,int key,int val)
{
if(l1==NULL)
return;
List* p=(List*)malloc(sizeof(List));
p->key=key;
p->val=val;
p->next=l1->next;
l1->next=p;
}
void Delete(List* l1,int key)
{
if(l1==NULL)
return;
struct List* p=l1;
struct List* q=p->next;
for(;p->next!=NULL;p=p->next)
{
if(p->next->key==key)
{
q=p->next;
p->next=q->next;
free(q);
break;//不加的话会有崩溃的风险
}
}
}
List* Search(List* l1,int key)
{
if(l1==NULL)
return NULL;
List* p=l1->next;
for(;p!=NULL;p=p->next)
{
if(p->key==key)
{
return p;
}
}
return NULL;
}
void Free(List* l1)
{
if(l1==NULL)
return;
List* p=l1->next;
while(l1->next!=NULL)
{
p=l1->next;
l1->next=p->next;
free(p);
}
}
typedef struct {
List* data;
} MyHashMap;
#define HSIZE 101
MyHashMap* myHashMapCreate() {
MyHashMap* myhash=(MyHashMap*)malloc(sizeof(MyHashMap));
myhash->data=(List*)malloc(sizeof(List)*HSIZE);
for(int i=0;i<HSIZE;i++)
{
myhash->data[i].next=NULL;
}
return myhash;
}
void myHashMapPut(MyHashMap* obj, int key, int value) {
if(obj==NULL)
return;
List* p=Search(&(obj->data[key%HSIZE]),key);
if(p==NULL)
{
Insret(&(obj->data[key%HSIZE]),key,value);
}
else
{
p->val=value;
}
}
int myHashMapGet(MyHashMap* obj, int key) {
if(obj==NULL)
return -1;
List* p=Search(&(obj->data[key%HSIZE]),key);
if(p==NULL)
return -1;
else
return p->val;
}
void myHashMapRemove(MyHashMap* obj, int key) {
if(obj==NULL)
return;
Delete(&(obj->data[key%HSIZE]),key);
}
void myHashMapFree(MyHashMap* obj) {
if(obj==NULL)
return;
for(int i=0;i<HSIZE;i++)
{
Free(&(obj->data[i]));
}
free(obj->data);
}
/**
* Your MyHashMap struct will be instantiated and called as such:
* MyHashMap* obj = myHashMapCreate();
* myHashMapPut(obj, key, value);
* int param_2 = myHashMapGet(obj, key);
* myHashMapRemove(obj, key);
* myHashMapFree(obj);
*/
心得:
一开始编写Search函数时,将其返回值设为int,即int Search(),返回结果为对应key值的value值,但在后续编写void myHashMapPut()函数的过程中发现,根据题目要求,我们需要获取对应key值的节点来修改value值,所以将Search函数的返回值改为List*。
在void Delete(List* l1,int key)函数中的for循环语句中,需要加上break,否则如果删除的是此链表的最后一个数据时,会再次执行循环语句,即使用空指针NULL,造成崩溃。