题目:
不使用任何内建的哈希表库设计一个哈希映射(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 101MyHashMap* 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;elsereturn 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,造成崩溃。