好文分享:c开源hash项目 uthash的用法总结_whatday的专栏-CSDN博客_uthash
目录
目录
查找HASH_FIIND_TYPE(INT/STR/PTR)
C 语言中,hash表通过结构体进行实现,hash表中每一个的元素,都使用结构体进行定义,结构体中的内容就是hash表一个元素所包含的内容
头文件
#include"uthash.h"
定义HASH表
在结构体中,务必带有UT_hash_handle hh;的句柄,key值只能有一个,但是context的值,可以有多个。
示例1:
typedef struct HashMap {
int val; // val保存下标
int key; // key保存值 UT hash 只能用键值进行查找 关键信息放键值
UT_hash_handle hh; // hash map的头
}HashMap;
示例2:
#define SIZE 10001
typedef struct Hash{
int index;
int count;
char key[SIZE]; // key 值
UT_hash_handle hh;
}Hash
前置准备
HashMap *hashHandle = NULL;
HashMap *tmp = (HashMap *)malloc(sizeof(HashMap));
//tmp也可以不再一开始声明空间
int类型为key值
HASH_ADD/FIND_INT
字符串类型为key值
HASH_ADD/FIND_STR
指针类型为key值
HASH_ADD/FIND_PTR
默认类型为void *
添加HASH_ADD_TYPE(INT/STR/PTR)
int类型
tmp = (HashMap *)malloc(sizeof(HashMap));
tmp->key = nums[i];
tmp->val = i;
HASH_ADD_INT(hashHandle, key, tmp);
// 第二个参数为Hash Map中你定义的key值的名称
// 第三个参数是你需要加入到HASH表中的内容
字符串类型
hashTmp = (Hash *)malloc(sizeof(Hash));
hashTmp->index = (*returnSize);
hashTmp->count = 1;
memset(hashTmp->key, 0, sizeof(hashTmp->key));
strcpy(hashTmp->key, tmp);
HASH_ADD_STR(head, key, hashTmp);
查找HASH_FIIND_TYPE(INT/STR/PTR)
int类型
int numTmp = target - nums[i];
HASH_FIND_INT(hashHandle, &numTmp, tmp);
HASH_FIND_INT(hashHandle, &numTmp, tmp);
// UT HASH 使用键值进行查询 第二个参数是键值的地址
// 第三个值为返回内容的存储区域,如果找到,则可以进行访问,如果没有找到,那么直接为NULL
字符串类型
char tmp[(len + 1)];
strcpy(tmp, strs[i]);
HASH_FIND_STR(head, tmp, hashTmp);
注意第二个参数的差异!
删除HASH_DEL
HASH_DEL(g_head, temp);
// 第一个依旧是Hash的头指针 第二个参数是要删除的元素
HashStruct *temp = NULL;
HASH_FIND_INT(g_head, &val, temp);
if (temp == NULL) {
return false;
}
HASH_DEL(g_head, temp); // UT hash 删除元素
free(temp); // 仅仅是在Hash map中移除,并没有free该变量
容量HASH_COUNT
int size = HASH_COUNT(head);
排序Sort
/* 多重条件排序 */
int cmp(Hash*s1, Hash *s2) {
if (s1->freq != s2->freq) {
return s1->freq < s2->freq;
}
return strcmp(s1->str, s2->str);
}
int main() {
HASH_SORT(g_head, cmp); //Hash自带的Sort
}
代表题目:642. 设计搜索自动补全系统
遍历
方式一:原版接口
HashStruct *currentUser = NULL;
HashStruct *temp = NULL;
HASH_ITER(hh, hashHead, currentUser, temp) {
// 遍历
}
//第一个参数未句柄,第二个参数为Hash head,第三个参数未当前元素,第四个就是temp
上题能够很好的历练UT_HASH的各种操作
方式二:自建数组
首先创建结构体
typedef struct Hash{
int times; // 出现频次
int val; // 数值
UT_hash_handle hh;
}Hash;
因为后续可能要对Hash map进行进一步的操作,书写必要的操作函数
int cmp (const void *s1, const void *s2) {
Hash *ag1 = *(Hash **)s1;
Hash *ag2 = *(Hash **)s2;
return ag1->times < ag2->times ? 1 : -1;
}
直接建立一个hash的数组,在C语言中这部分非常灵活。
Hash **numTimes = (Hash **)malloc(sizeof(Hash *) * 5000);
int index = 0;
for (int i = 0; i < numsSize; ++i) {
HASH_FIND_INT(head, &nums[i], tmp);
if (tmp != NULL) {
tmp->times++;
} else {
numTimes[index] = (Hash *)malloc(sizeof(Hash));
numTimes[index]->val = nums[i];
numTimes[index]->times = 1;
HASH_ADD_INT(head, val, numTimes[index]);
index++;
}
}
qsort(numTimes, index, sizeof(Hash *), cmp);
完整代码如下:
二维数组版本:
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
typedef struct Hash{
int times; // 出现频次
int val; // 数值
UT_hash_handle hh;
}Hash;
int cmp (const void *s1, const void *s2) {
Hash *ag1 = *(Hash **)s1;
Hash *ag2 = *(Hash **)s2;
return ag1->times < ag2->times ? 1 : -1;
}
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize){
if (nums == NULL) {
return NULL;
}
Hash *head = NULL;
Hash *tmp = NULL;
Hash **numTimes = (Hash **)malloc(sizeof(Hash *) * 5000);
int index = 0;
for (int i = 0; i < numsSize; ++i) {
HASH_FIND_INT(head, &nums[i], tmp);
if (tmp != NULL) {
tmp->times++;
} else {
// printf("nums[i] = %d\n", nums[i]);
numTimes[index] = (Hash *)malloc(sizeof(Hash));
numTimes[index]->val = nums[i];
numTimes[index]->times = 1;
HASH_ADD_INT(head, val, numTimes[index]);
index++;
}
}
qsort(numTimes, index, sizeof(numTimes[0]), cmp);
int *res = (int *)malloc(sizeof(int) * k);
memset(res, 0, sizeof(int) * k);
(*returnSize) = 0;
for (int i = 0; i < k; ++i) {
res[(*returnSize)++] = numTimes[i]->val;
}
return res;
}
一维数组
// 排序算法
typedef struct Hash{
int freq;
int val;
UT_hash_handle hh;
}Hash;
int cmp(void *s1, void *s2) {
Hash *arg1 = (Hash *)s1;
Hash *arg2 = (Hash *)s2;
return arg1->freq < arg2->freq;
}
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize){
if (nums == NULL) {
return NULL;
}
(*returnSize) = 0;
Hash *head = NULL;
Hash item[numsSize]; // 使用指针数组也可以
int index = 0;
for (int i = 0; i < numsSize; ++i) {
Hash *temp = NULL;
HASH_FIND_INT(head, &nums[i], temp);
if (temp != NULL) {
temp->freq++;
} else {
item[index].freq = 1;
item[index].val = nums[i];
HASH_ADD_INT(head, val, &item[index]);
index++;
}
}
qsort(item, index, sizeof(Hash), cmp);
int *res = (int *)malloc(sizeof(int) * k);
for (int i = 0; i < k; ++i) {
res[(*returnSize)++] = item[i].val;
}
return res;
}
最好最好不要用!指针数组 Hash *item[n];
自定义类型查找
当int和str都不能满足要求的时候,我们需要自行定义结构体
typedef struct Pair{
int x;
int y;
} Pair;
typedef struct Hash{
Pair point;
UT_hash_handle hh;
} Hash;
ADD
HASH_ADD(hh, head, point, sizeof(Pair), temp);
FIND
HASH_FIND(hh, head, &point, sizeof(Pair), temp);
其余都一样
遍历:
Hash *cur = NULL;
HASH_ITER(hh, g_head, cur, temp) {
printf("cur->point.x = %d, cur->point.y = %d\n", cur->point.x, cur->point.y);
}
练习:
#define LEN 4
int direct;
typedef struct Pair{
int x;
int y;
} Pair;
typedef struct Hash{
Pair point;
UT_hash_handle hh;
} Hash;
Hash *g_head = NULL;
void GoGOPoint(int commands, int *point) {
Pair step;
Hash *temp = NULL;
if (direct == 0) {
// 北 point[1] += commands;
step.x = point[0];
for (int i = 0; i < commands; ++i) {
step.y = point[1] + 1;
HASH_FIND(hh, g_head, &step, sizeof(Pair), temp);
if (temp != NULL) {
break;
} else {
point[1]++;
}
}
} else if (direct == 1) {
// 东
step.y = point[1];
for (int i = 0; i < commands; ++i) {
step.x = point[0] + 1;
HASH_FIND(hh, g_head, &step, sizeof(Pair), temp);
if (temp != NULL) {
break;
} else {
point[0]++;
}
}
} else if (direct == 2) {
// 南
step.x = point[0];
for (int i = 0; i < commands; ++i) {
step.y = point[1] - 1;
HASH_FIND(hh, g_head, &step, sizeof(Pair), temp);
if (temp != NULL) {
break;
} else {
point[1]--;
}
}
} else if (direct == 3) {
// 西
step.y = point[1];
for (int i = 0; i < commands; ++i) {
step.x = point[0] - 1;
HASH_FIND(hh, g_head, &step, sizeof(Pair), temp);
if (temp != NULL) {
break;
} else {
point[0]--;
}
}
}
return;
}
void MovePoint(int commands, int *point) {
if (commands == -2) {
direct = (LEN + direct - 1) % LEN;
}
if (commands == -1) {
direct = (LEN + direct + 1) % LEN;
}
if (commands >= 1 && commands <= 9) {
GoGOPoint(commands, point);
}
return;
}
int robotSim(int* commands, int commandsSize, int** obstacles, int obstaclesSize, int* obstaclesColSize){
if (NULL == commands) {
return 0;
}
direct = 0; // 北 0 1东 2南 3西
int size = commandsSize;
int point[2];
memset(point, 0, sizeof(point));
// 做一张障碍物地图
// memset(map, 0, sizeof(map));
g_head = NULL;
Hash *temp = NULL;
for (int i = 0; i < obstaclesSize; i++) {
Hash *temp = (Hash *)malloc(sizeof(Hash));
temp->point.x = obstacles[i][0];
temp->point.y = obstacles[i][1];
HASH_ADD(hh, g_head, point, sizeof(Pair), temp);
}
int res = 0;
for (int i = 0; i < size; ++i) {
MovePoint(commands[i], point);
// printf("com = %d, direct = %d, point[0] = %d, point[1] = %d\n",
// commands[i], direct, point[0], point[1]);
int dis = (point[0] * point[0]) + (point[1] * point[1]);
res = fmax(res, dis);
}
return res;
}
LC练习
INT 类型习题
typedef struct HashMap {
int val; // val保存下标
int key; // key保存值 UT hash 只能用键值进行查找 关键信息放键值
UT_hash_handle hh; // hash map的头
}HashMap;
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
if (nums == NULL) {
return NULL;
}
returnSize[0] = 2;
int *res = (int *)malloc(sizeof(int) * 2);
HashMap *hashHandle = NULL;
HashMap *tmp = (HashMap *)malloc(sizeof(HashMap));
tmp->val = -1;
tmp->key = INT_MIN;
for (int i = 0;i < numsSize;++i) {
int numTmp = target - nums[i];
HASH_FIND_INT(hashHandle, &numTmp, tmp);
if (tmp != NULL) {
res[0] = tmp->val;
res[1] = i;
return res;
}
tmp = (HashMap *)malloc(sizeof(HashMap));
tmp->key = nums[i];
tmp->val = i;
HASH_ADD_INT(hashHandle, key, tmp);
}
returnSize[0] = 0;
return res;
}
STR类型练习
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
#define SIZE 5000
int cmp (const void *s1, const void *s2) {
return *(char *)s1 > *(char *)s2;
}
typedef struct Hash{
int index; // res中的下标 res的一级下标
int count; // 相同元素的个数 res的二级下标
char keyStr[SIZE]; // key
int len; // 单词长度
UT_hash_handle hh;
}Hash;
char *** groupAnagrams(char ** strs, int strsSize, int* returnSize, int** returnColumnSizes){
// 整体思路 内部qsort 加入到hash中,遇到相同的直接找下标即可,然后遍历hash表
char ***res = (char ***)malloc(sizeof(char **) * strsSize);
(*returnSize) = 0;
returnColumnSizes[0] = (int *)malloc(sizeof(int) * strsSize);
Hash *head = NULL;
Hash *headTmp = (Hash *)malloc(sizeof(Hash));
for (int i = 0; i < strsSize;++i) {
char tmp[strlen(strs[i]) + 1];
memset(tmp, 0, sizeof(tmp));
strcpy(tmp, strs[i]);
qsort(tmp, strlen(tmp), sizeof(char), cmp);
HASH_FIND_STR(head, tmp, headTmp);
if (headTmp == NULL) {
// 新增加一个
headTmp = (Hash *)malloc(sizeof(Hash));
memset(headTmp->keyStr, 0, sizeof(headTmp->keyStr));
strcpy(headTmp->keyStr, tmp); // 最大的细节 一定要保存排好序的内容
headTmp->index = (*returnSize);
headTmp->count = 1;
headTmp->len = strlen(strs[i]) + 1;
HASH_ADD_STR(head, keyStr, headTmp);
res[(*returnSize)] = (char **)malloc(sizeof(char *) * strsSize);
res[(*returnSize)][0] = (char *)malloc(sizeof(char) * headTmp->len);
memset(res[(*returnSize)][0], 0, sizeof(char) * headTmp->len);
memcpy(res[*returnSize][0], strs[i], strlen(strs[i]));
returnColumnSizes[0][(*returnSize)] = 1;
(*returnSize)++;
} else {
// 找到了
res[headTmp->index][headTmp->count] = (char *)malloc(sizeof(char) * headTmp->len);
memset(res[headTmp->index][headTmp->count], 0, sizeof(char) * headTmp->len);
memcpy(res[headTmp->index][headTmp->count], strs[i], strlen(strs[i]));
returnColumnSizes[0][headTmp->index] = headTmp->count + 1;
headTmp->count = headTmp->count + 1;
}
}
return res;
}
以INT为key值:
#define SIZE 5000
typedef struct Hash {
int key; // key
struct TreeNode* father;
struct TreeNode* self;
UT_hash_handle hh;
}Hash;
Hash *g_head;
int *g_res;
void GetTreeInfo (struct TreeNode* root, struct TreeNode* father) {
if (root == NULL) {
return;
}
// 记录每个结点的父亲
Hash *node = (Hash *)malloc(sizeof(Hash));
node->key = root->val; // 记录本结点的值
node->father = father; // 记录父亲
node->self = root; // 记录自己
HASH_ADD_INT(g_head, key, node);
GetTreeInfo(root->left, root);
GetTreeInfo(root->right, root);
}
void DFS (struct TreeNode* root, struct TreeNode* form, int k, int* returnSize) {
if (root == NULL) {
return;
}
if (k == 0) {
g_res[(*returnSize)++] = root->val;
return;
}
if (root->left != form) {
DFS(root->left, root, k - 1, returnSize); // left
}
if (root->right != form) {
DFS(root->right,root, k - 1, returnSize); // right
}
Hash *tmp = NULL;
HASH_FIND_INT(g_head, &root->val, tmp);
if (tmp != NULL && tmp->father != form) {
DFS(tmp->father, root, k - 1, returnSize); // head
}
}
int* distanceK(struct TreeNode* root, struct TreeNode* target, int k, int* returnSize) {
if (root == NULL) {
return NULL;
}
g_head = NULL;
// 遍历一遍树,把每个元素的父结点都记录下来
GetTreeInfo(root, NULL);
g_res = (int *)malloc(sizeof(int) * SIZE);
(*returnSize) = 0;
// 遍历一遍,找到目标值
Hash *tmp = NULL;
HASH_FIND_INT(g_head, &target->val, tmp);
if (tmp == NULL) {
return g_res;
}
struct TreeNode* begin = tmp->self;
DFS(begin, NULL, k, returnSize);
return g_res;
}
整体接口练习
typedef struct _HashNode{
int key;
UT_hash_handle hh;
} HashNode;
typedef struct {
HashNode *g_hashNode;
} RandomizedSet;
/** Initialize your data structure here. */
RandomizedSet* randomizedSetCreate() {
RandomizedSet *obj = (RandomizedSet*)malloc(sizeof(RandomizedSet));
memset(obj, 0, sizeof(RandomizedSet));
return obj;
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool randomizedSetInsert(RandomizedSet* obj, int val) {
HashNode *s = NULL;
HASH_FIND_INT(obj->g_hashNode, &val, s);
if (s != NULL) {
return false;
}
s = (HashNode*)malloc(sizeof(HashNode));
s->key = val;
HASH_ADD_INT(obj->g_hashNode, key, s);
return true;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
bool randomizedSetRemove(RandomizedSet* obj, int val) {
HashNode *hash = NULL;
HASH_FIND_INT(obj->g_hashNode, &val, hash);
if (hash == NULL) {
return false;
}
HASH_DEL(obj->g_hashNode, hash);
free(hash);
return true;
}
/** Get a random element from the set. */
int randomizedSetGetRandom(RandomizedSet* obj) {
HashNode *cur = NULL;
HashNode *tmp = NULL;
int idx = 0;
int cnt = 0;
idx = rand() % (HASH_COUNT(obj->g_hashNode));
HASH_ITER(hh, obj->g_hashNode, cur, tmp) {
if (cnt++ >= idx) {
break;
}
}
return cur->key;
}
void randomizedSetFree(RandomizedSet* obj) {
HashNode *cur = NULL;
HashNode *tmp = NULL;
HASH_ITER(hh, obj->g_hashNode, cur, tmp) {
HASH_DEL(obj->g_hashNode, cur);
free(cur);
}
free(obj);
}
#define LEN 1000
typedef struct {
char str[LEN];
char strHead[2];
int freq;
UT_hash_handle hh;
} AutocompleteSystem;
char g_searchHis[LEN] = {0};
int g_size = 0;
AutocompleteSystem *g_head = NULL;
/* 向Hash表中增加元素 */
void AddNode(char *strVal, int freqVal) {
AutocompleteSystem *temp = NULL;
temp = (AutocompleteSystem *)malloc(sizeof(AutocompleteSystem));
memset(temp->str, 0, sizeof(temp->str));
memset(temp->strHead, 0, sizeof(temp->strHead));
temp->strHead[0] = strVal[0];
strcpy(temp->str, strVal);
temp->freq = freqVal;
HASH_ADD_STR(g_head, str, temp);
}
AutocompleteSystem* autocompleteSystemCreate(char ** sentences, int sentencesSize, int* times, int timesSize) {
int row = sentencesSize;
g_size = row;
for (int i = 0; i < row; ++i) {
AddNode(sentences[i], times[i]);
}
return g_head;
}
/* 打印函数 */
void Printf() {
printf("PRINTF\n");
AutocompleteSystem *curItem = NULL;
AutocompleteSystem *temp = NULL;
HASH_ITER(hh, g_head, curItem, temp) {
printf("curItem->str = %s, curItem->freq = %d\n", curItem->str, curItem->freq);
}
}
/* 以出现频次作为索引,降序排序,如果频次相同,那么改为以字典序升序排序 */
int cmp(AutocompleteSystem *s1, AutocompleteSystem *s2) {
if (s1->freq != s2->freq) {
return s1->freq < s2->freq;
}
return strcmp(s1->str, s2->str);
}
char ** autocompleteSystemInput(AutocompleteSystem* obj, char c, int* retSize) {
// 先排序
HASH_SORT(g_head, cmp);
// Printf();
retSize[0] = 0;
char **res = (char **)malloc(sizeof(char *) * g_size);
/* 保存临时输入字符,并转化为字符串,方便后续操作 */
char cTmp[2] = {0};
cTmp[0] = c;
if (c == '#') {
// 之前搜的要记下来,此处全部压入Hash,重复搜索还要增加搜索次数
AutocompleteSystem *temp = NULL;
HASH_FIND_STR(g_head, g_searchHis, temp);
if (temp != NULL) {
temp->freq++;
} else {
AddNode(g_searchHis, 1);
}
memset(g_searchHis, 0, sizeof(g_searchHis));
return res;
} else {
strcat(g_searchHis, cTmp);// 记录搜索记录
AutocompleteSystem *curItem = NULL;
AutocompleteSystem *temp = NULL;
HASH_ITER(hh, g_head, curItem, temp) {
if (retSize[0] == 3) {
break;
}
// 搜索首字母
char *strTmp = NULL;
strTmp = strstr(curItem->str, g_searchHis); // 查找当前搜索是否有对应的历史记录
/* 历史记录中如果存在,那么必须是前缀完全相同strTmp == curItem->str必不可少 */
if (strTmp != NULL && strTmp == curItem->str) {
res[retSize[0]] = (char *)malloc(sizeof(char) * (strlen(curItem->str) + 1));
strcpy(res[retSize[0]], curItem->str);
retSize[0]++;
}
}
}
return res;
}
void autocompleteSystemFree(AutocompleteSystem* obj) {
AutocompleteSystem *curItem = NULL;
AutocompleteSystem *temp = NULL;
HASH_ITER(hh, g_head, curItem, temp) {
HASH_DEL(g_head, curItem);
free(curItem);
}
}
/**
* Your AutocompleteSystem struct will be instantiated and called as such:
* AutocompleteSystem* obj = autocompleteSystemCreate(sentences, sentencesSize, times, timesSize);
* char ** param_1 = autocompleteSystemInput(obj, c, retSize);
* autocompleteSystemFree(obj);
*/