赞
踩
目录
3、C语言开源项目uthash.h 中的hash接口 使用指南
哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说,
当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
哈希表常用来搜索查找需要的元素, 哈希表的查找时间复杂度通常很低, 这里没有深入研究他的复杂度如何计算。
实现hash这种数据结构, 主要考虑两点:
(1)哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上。
(2)冲突处理:由于不同元素可能映射到相同的整数值,因此需要在整数值出现「冲突」时,需要进行冲突处理。总的来说,有以下几种策略解决冲突:
本文使用链表的方式存储相同hash哈希值的元素, 因此代码中有链表的常见操作, 如链表创建、插入、删除、查找。
基于链表的操作, 完成了hash表的 创建、查找、插入、删除等操作。
代码如下:
-
- typedef struct _ListNode_{
- int val;
- struct _ListNode_ *next;
- } stListNode;
-
- typedef struct {
- stListNode *data;
- } MyHashSet;
-
- /** Initialize your data structure here. */
- //参考官方题解-- 用链表存储每一个key对应的不同的value,也就是解决冲突
-
-
- /* 先完成链表的基本操作 */
- // 在头结点之后插入一个新的节点
- void ListPush(stListNode *head, int key)
- {
- stListNode *tmp = (stListNode *)malloc(sizeof(stListNode));
-
- tmp->val = key;
- tmp->next = head->next;
- head->next = tmp;
- }
-
-
- void ListDelete(stListNode *head, int key)
- {
- stListNode *it = head;
- stListNode *tmp = NULL;
-
- while(it->next)
- {
- if(key == it->next->val)
- {
- tmp = it->next;
- it->next = tmp->next;
- free(tmp);
- break;
- }
-
- it = it->next;
- }
- }
-
- //查找链表中是否存在key, 存在返回1, 不存在返回0
- int ListContains(stListNode *head, int x)
- {
- stListNode *it = head;
-
- //这里必须是it->next开头, 不然的话就会在头一次调用时判断失误, 具体原因不清楚???
- while(it->next)
- {
- if(it->next->val == x)
- {
- return 1;
- }
-
- it = it->next;
- }
-
- return 0;
- }
-
- //删除链表的所有节点
- void listFree(stListNode *head)
- {
- while(head->next)
- {
- stListNode *tmp = head->next;
- head->next = tmp->next;
- free(tmp);
- }
- }
-
- const int base = 769;
-
- int hash(int key)
- {
- return key % base;
- }
-
-
- //创建一个hash表, 里面可以存储base个key键值桶
- MyHashSet* myHashSetCreate(void)
- {
- MyHashSet *rethash = (MyHashSet *)malloc(sizeof(MyHashSet));
- rethash->data = (stListNode *)malloc(sizeof(stListNode) * base);
-
- for(int i=0; i<base; i++)
- {
- rethash->data[i].val = 0;
- rethash->data[i].next = NULL;
- }
-
- return rethash;
- }
-
- //插入时先判断是否存在, 不存在时再插入
- void myHashSetAdd(MyHashSet* obj, int key)
- {
- int h = hash(key);
-
- if( !ListContains(&(obj->data[h]), key) )
- {
- ListPush(&(obj->data[h]), key);
- }
- }
-
- void myHashSetRemove(MyHashSet* obj, int key)
- {
- int h = hash(key);
- ListDelete(&(obj->data[h]), key);
- }
-
- /** Returns true if this set contains the specified element */
- bool myHashSetContains(MyHashSet* obj, int key)
- {
- //找到键值桶, 在链表中查找是否存在
- int h = hash(key);
- return ListContains (&(obj->data[h]), key );
- }
-
- //删除base所有桶的list链表
- void myHashSetFree(MyHashSet* obj)
- {
- for(int i=0; i<base; i++)
- {
- listFree(&(obj->data[i]));
- }
-
- free(obj->data);
- }
-
- /**
- * Your MyHashSet struct will be instantiated and called as such:
- * MyHashSet* obj = myHashSetCreate();
- * myHashSetAdd(obj, key);
-
- * myHashSetRemove(obj, key);
-
- * bool param_3 = myHashSetContains(obj, key);
-
- * myHashSetFree(obj);
- */
有关hash集合和hash表的设计, 详见
uthash.h 是C语言的开源项目,它实现了常见的hash操作函数,例如查找、插入、删除等。该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构最为key值,甚至可以采用多个值作为key,无论是自定义的struct还是基本数据类型,需要注意的是不同类型的key其操作接口方式略有不通。
使用uthash代码时只需要包含头文件"uthash.h"即可。由于该代码采用宏的方式实现,所有的实现代码都在uthash.h文件中,因此只需要在自己的代码中包含该头文件即可。可以通过下面两种方式获取源代码:
能够实现哪些功能?
接口 | 含义 | 使用方法&功能 | 备注 |
---|---|---|---|
UT_hash_handle hh | hh是内部使用的hash处理句柄 | 使用时只需要在结构体中定义一个字段,不需要对该变量赋值
| |
HASH_FIND_INT | 查找键值接口,对应的是int 整型的键值key HASH_FIND_INT(head, key_ptr, item_ptr) | 键值查找,
入参1一般是一个全局的hash表 入参2一般是待查找的键值的地址,入参3是输出参数,将查找得到的结果赋值到这里 | |
HASH_ADD_INT | 插入键值的接口,对应的是int 整型的键值key
HASH_ADD_INT(head, keyfield_name, item_ptr) | 键值插入到hash表中, HASH_ADD_INT(g_users, key, s ); /* 这里必须明确告诉插入函数,自己定义的hash结构体中键变量的名字 */ | 插入之前要想find查找一下该元素是否已经存在 |
HASH_DEL | 删除hash键值的接口 HASH_DEL(head, item_ptr) | 需要告诉该接口要释放哪个hash表(这里是
| 删除所有节点,可以调用遍历节点的接口,遍历到之后依次删除
|
HASH_FIND_PTR | 使用地址void *类型作为键值时的查找接口 HASH_FIND_PTR(head, key_ptr, item_ptr)
| 可以用在结构体指针作为key的时候, 比如链表节点结构体作为key | |
HASH_ADD_PTR | 使用地址void *类型作为键值时的插入接口 HASH_ADD_PTR(head, keyfield_name, item_ptr) | 可以用在结构体指针作为key的时候, 比如链表节点结构体作为key | |
HASH_FIND_STR | 查找接口, key键值是一个数组 HASH_FIND_STR(head, key_ptr, item_ptr) | HASH_FIND_STR(g_strHash, s, tmp); s是数组的首地址 | 具体可以参考 |
HASH_ADD_STR | 插入接口, key键值是一个数组 HASH_ADD_STR(head, keyfield_name, item_ptr) | HASH_ADD_STR(g_strHash, str, it); str是结构体定义中的数字名字 | |
HASH_COUNT | 统计hash表中的已经存在的元素数 HASH_COUNT(head) |
| |
HASH_ITER | 遍历所有hash元素 HASH_ITER(hh_name, head, item_ptr, tmp_item_ptr) | struct my_struct *current_user, *tmp; HASH_ITER(hh, users, current_user, tmp){ ....... } 遍历得到的元素存放在current_user变量中 用于依次删除元素,或者打印所有元素 | |
HASH_SORT | 哈希排序 HASH_SORT(head, cmp) | ||
HASH_FIND | 通用查找接口 HASH_FIND(hh_name, head, key_ptr, key_len, item_ptr) | ||
HASH_ADD | 通用添加接口 HASH_ADD(hh_name, head, keyfield_name, key_len, item_ptr) |
看一道LeetCode上的典型题目, 求两个数组的交集。 链接 LC349. 两个数组的交集
- /**
- * Note: The returned array must be malloced, assume caller calls free().
- */
-
- //哈希集合法----这次写的封装接口, 把全局的hash表当做入参了
- typedef struct _HashSet_{
- int key;
- UT_hash_handle hh;
- }stHashSet;
-
-
- stHashSet *find(stHashSet** hashtable, int ikey) {
- stHashSet* tmp;
- HASH_FIND_INT(*hashtable, &ikey, tmp);
- return tmp;
- }
-
- void insert(stHashSet** hashtable, int ikey)
- {
- stHashSet* tmp = find(hashtable, ikey);
- if (tmp != NULL) return;
- tmp = malloc(sizeof(stHashSet));
- tmp->key = ikey;
- HASH_ADD_INT(*hashtable, key, tmp);
- }
-
- int *func(stHashSet** hashSet1, stHashSet** hashSet2, int* returnSize)
- {
- if(HASH_COUNT(*hashSet1) > HASH_COUNT(*hashSet2))
- {
- func(hashSet2, hashSet1, returnSize);
- }
-
- //储存结果的地方申请内存
- int *retArr = (int *)malloc(sizeof(int)*HASH_COUNT(*hashSet2));
- *returnSize = 0;
-
- //遍历元素少的那个hash表, 在元素多的表中查找是否存在相同的key
- stHashSet *tmp, *it;
- HASH_ITER(hh, *hashSet1, tmp, it)
- {
- if( find(hashSet2, tmp->key) )
- {
- retArr[*returnSize] = tmp->key;
- (*returnSize)++;
- }
- }
-
- return retArr;
- }
-
-
- int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize)
- {
- int i = 0;
- stHashSet *hSet1 = NULL;
- stHashSet *hSet2 = NULL;
-
- /*插入元素、构造两个数组的hash集合*/
- for(i=0; i<nums1Size; i++)
- {
- insert(&hSet1, nums1[i]);
- }
-
- for(i=0; i<nums2Size; i++)
- {
- insert(&hSet2, nums2[i]);
- }
-
- return func(&hSet1, &hSet2, returnSize);
-
- }
LeetCode链接:LeetCode哈希表book
c开源hash项目 uthash的用法总结
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。