leecode刷题算法总结----哈希表
一、哈希表常用函数
结构构建
//哈希表结构体的构建
//1、key值为int型
struct HashEntry_int {
int key; /* key */
UT_hash_handle hh; /* makes this structure hashable */
};
//2、key值为字符串
struct HashEntry_str {
char* key; /* key */
UT_hash_handle hh; /* makes this structure hashable */
};
//3、key值为万能值(这里用long long代替)
struct HashEntry_ll {
long long key; /* key */
UT_hash_handle hh; /* makes this structure hashable */
};
哈希表增加
//HASH_ADD_INT表示添加的键值为int类型
//HASH_ADD_STR表示添加的键值为字符串类型
//HASH_ADD_PTR表示添加的键值为指针类型
//HASH_ADD表示添加的键值可以是任意类型
//哈希表添加模板 int型
void HashAddItem_int(struct HashEntry_int **head, int key)
{
struct HashEntry_int * temp;
//申请节点空间
temp = malloc(sizeof(struct HashEntry_int));
//节点参数赋值
temp->key = key;
//添加到哈希表中
HASH_ADD_INT(*head,key,temp);
}
//哈希表添加模板 字符串型
void HashAddItem_str(struct HashEntry_str **head, char *s)
{
struct HashEntry_str * temp;
//申请节点空间
temp = malloc(sizeof(struct HashEntry_str));
//节点参数赋值
temp->key = malloc(sizeof(char)*(strlen(s)+1));
strcpy(temp->key, s);
//添加到哈希表中
HASH_ADD_STR(*head, key, temp);
}
//哈希表添加模板 万能型 (long long 代替)
void HashAddItem_ll(struct HashEntry_ll **head, long long key)
{
struct HashEntry_ll * temp;
//申请节点空间
temp = malloc(sizeof(struct HashEntry_ll));
//节点参数赋值
temp->key = key;
//添加到哈希表中
HASH_ADD(hh,*head,key,sizeof(long long),temp);
}
哈希表查找
// 哈希查找模板 int型
struct HashEntry_int* find_int(struct HashEntry_int **head, int key)
{
struct HashEntry_int* temp = NULL;
HASH_FIND_INT(*head, &key, temp);
return temp;
}
struct HashEntry_str* find_str(struct HashEntry_str **head, char* s)
{
struct HashEntry_str* temp = NULL;
HASH_FIND_STR(*head, s, temp);
return temp;
}
// 哈希查找模板 int型
struct HashEntry_ll* find_ll(struct HashEntry_ll **head, long long key)
{
struct HashEntry_ll* temp = NULL;
HASH_FINDl(hh, *head, &key, sizeof(long long) temp);
return temp;
}
哈希表遍历
//哈希表遍历
// HASH_ITER(hh_name, head, item_ptr, tmp_item_ptr)
//两种通用
struct HashEntry *curr, *next;
HASH_ITER(hh, *obj, curr, next)
{
//执行要做的操作
}
//举例
//如删除
void hashFreeAll(struct HashEntry_str **head)
{
struct HashEntry_str *cur, *next;
HASH_ITER(hh, *head, cur, next){
//执行要做的操作
free(cur->key);
HASH_DEL(*head,cur);
free(cur);
}
}
void hashFreeAll(struct HashEntry_int **head)
{
struct HashEntry_int *curr, *next;
HASH_ITER(hh, *head, curr, next){
//执行要做的操作
HASH_DEL(*head,curr);
free(curr);
}
}
void hashFreeAll(struct HashEntry_ll **head)
{
struct HashEntry_ll *curr, *next;
HASH_ITER(hh, *head, curr, next){
//执行要做的操作
HASH_DEL(*head,curr);
free(curr);
}
}
其他函数详情请见下面链接
二、实战模板使用
ps:(说明,题目可能有更好的解法,但是为了展示哈希表的使用,就使用hash表解题)
字符串模板
884. 两句话中的不常见单词
解题思路:
两句话中只出现一次的单词算不常见单词
所以只需要将两句话的单词分别加入哈希表中,并且统计该单词的出现次数,将次数为1的单进行输出
第一步、确定哈希结构
struct HashEntry{
char *key; //字符串键值
int times; //出现次数
UT_hash_handle hh;
};
第二步、将字符串1和字符串2加入哈希表中
// 需要使用 查找和添加函数
自定义插入函数写逻辑 逻辑如下
对于一个字符串,先在哈希表中查找,如果找到了数量+1,没找到就加入哈希表中
第三步、遍历哈希表,将次数为1的字符串进行输出
代码部分(直接套模板):
#define MAX 200
struct HashEntry_str{
char *key;
int times;
UT_hash_handle hh;
};
struct HashEntry_str* find_str(struct HashEntry_str **head, char* s)
{
struct HashEntry_str* temp = NULL;
HASH_FIND_STR(*head, s, temp);
return temp;
}
//哈希表添加模板 字符串型
void HashAddItem_str(struct HashEntry_str **head, char *s)
{
struct HashEntry_str * temp;
//申请节点空间
temp = malloc(sizeof(struct HashEntry_str));
//节点参数赋值
temp->times = 1;
temp->key = malloc(sizeof(char)*(strlen(s)+1));
strcpy(temp->key, s);
//添加到哈希表中
HASH_ADD_STR(*head, key, temp);
}
void insertHash(struct HashEntry_str **head, char *s)
{
struct HashEntry_str * temp = NULL;
temp = find_str(head,s);
if(temp){
temp->times++;
}else{
HashAddItem_str(head,s);
}
}
void hashFreeAll(struct HashEntry_str **head)
{
struct HashEntry_str *cur, *next;
HASH_ITER(hh, *head, cur, next){
//执行要做的操作
free(cur->key);
HASH_DEL(*head,cur);
free(cur);
}
}
char ** uncommonFromSentences(char * s1, char * s2, int* returnSize){
char **ans = malloc(sizeof(char*)*MAX);
*returnSize = 0;
struct HashEntry_str *head = NULL;
//添加字符串到哈希表中
char *c = strtok(s1," ");
while(c != NULL){
insertHash(&head,c);
c = strtok(NULL, " ");
}
c = strtok(s2," ");
while(c != NULL){
insertHash(&head,c);
c = strtok(NULL, " ");
}
//遍历哈希表
struct HashEntry_str *cur,*next;
HASH_ITER(hh,head,cur,next){
if(cur->times == 1){
ans[(*returnSize)] = malloc(sizeof(char)*(strlen(cur->key)+1));
strcpy(ans[(*returnSize)++],cur->key);
}
}
hashFreeAll(&head);
return ans;
}
int值模板
1. 两数之和
解题思路:
先把数组中的元素加入到哈希表中,然后遍历数组,查找对应和为target的值,如果找到了就返回两个数值对应的下标
第一步、确认哈希结构
struct HashEntry_int {
int key; /* key */
int pos; /* 下标 */
UT_hash_handle hh; /* makes this structure hashable */
};
第二步、在哈希表中查找相加等于target的值
如果有值返回该值下标和当前下标
如果没有值 将当前值和下标加入哈希表中
代码部分
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
struct HashEntry_int {
int key; /* key */
int pos; /* 下标 */
UT_hash_handle hh; /* makes this structure hashable */
};
// 哈希表添加模板 int型
// 对添加函数稍微做了一些调整,多了一个参数
void HashAddItem_int(struct HashEntry_int **head, int key, int pos)
{
struct HashEntry_int * temp;
//申请节点空间
temp = malloc(sizeof(struct HashEntry_int));
//节点参数赋值
temp->key = key;
temp->pos = pos;
//添加到哈希表中
HASH_ADD_INT(*head,key,temp);
}
// 哈希查找模板 int型
struct HashEntry_int* find_int(struct HashEntry_int **head, int key)
{
struct HashEntry_int* temp = NULL;
HASH_FIND_INT(*head, &key, temp);
return temp;
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int *ans = malloc(sizeof(int)*2);
*returnSize = 0;
struct HashEntry_int* head;
struct HashEntry_int* temp = NULL;
for(int i = 0; i < numsSize; i++){
// 查找符合目标的值
temp = find_int(&head,target-nums[i]);
if(temp){
// 找到了,赋值
ans[0] = temp->pos;
ans[1] = i;
(*returnSize) = 2;
break;
}else{
// 没找到,把当前值加入哈希表中
HashAddItem_int(&head,nums[i],i);
}
}
return ans;
}
万能值模板
1001. 网格照明
解题思路
创建5个哈希表分别为
points 存储亮灯的点 --> key值为经过xy计算后的值
rows 存储亮灯的行 --> key值为所在行的值
cols 存储亮灯的列 --> key值为所在列的值
diagonal 存储亮灯的左上到右下\ --> key值为x-y 因为每一条斜线中的xy的差值相等
antiDiaginal 存储亮灯的右上到左下/ --> key值为x+y 因为每一条斜线中的xy的和值相等
先点灯,然后点亮该位置的灯之后,将其所能照亮的四条线路的亮度+1,如果有重复出现的,则不点灯。然后再对该位置周围的九个位置进行遍历,如果这些位置有灯,就灭掉,并且把这个灯所能照亮的区域的亮度-1 在遍历的过程中,如果该位置所处的四个区域任意一个位置亮度大于1,则该位置为1,否则为0;
第一步、确定哈希结构
typedef struct {
long long key;
int val; // 用来存储亮度 有一个灯照亮为亮度1 两个灯照亮为亮度2
UT_hash_handle hh;
} HashEntry;
第二步 点灯
// 把灯放进去
for (int i = 0; i < lampsSize; i++) {
// 避免多次列出时, 值重复累加, 已经加过的值就不再加入
temp = FindHash(&points, ((long long)GetKey(lamps[i][0], lamps[i][1])));
if (temp) {
continue;
}
InsertHash(&points, (long long)GetKey(lamps[i][0], lamps[i][1]));
InsertHash(&rows, (long long)lamps[i][0]);
InsertHash(&cols, (long long)lamps[i][1]);
InsertHash(&diagonal, (long long)(lamps[i][0] - lamps[i][1]));
InsertHash(&antiDiaginal, (long long)(lamps[i][0] + lamps[i][1]));
}
第三步 存放结果 第四步 关灯
for (int i = 0; i < queriesSize; i++) {
if (IsLight(&rows, &cols, &diagonal, &antiDiaginal, queries[i][0], queries[i][1])) {
ans[(*returnSize)++] = 1;
} else {
ans[(*returnSize)++] = 0;
}
// 关灯
CloseLight(&points, &rows, &cols, &diagonal, &antiDiaginal, n, queries[i][0], queries[i][1]);
}
第五步 返回结果
return ans;
代码部分
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
/*
哈希表 创建五个哈希表
分别为 points 存储亮灯的点 rows 存储亮灯的行 cols 存储亮灯的列 diagonal 存储亮灯的左上到右下\ antiDiaginal 存储亮灯的右上到左下/
*/
typedef struct {
long long key;
int val; // 用来存储亮度 有一个灯照亮为亮度1 两个灯照亮为亮度2
UT_hash_handle hh;
} HashEntry;
long long GetKey(int x, int y)
{
return (long long)x + ((long long)y << 32);
}
void AddHash(HashEntry **head, long long key)
{
HashEntry *temp = NULL;
temp = malloc(sizeof(HashEntry));
temp->key = key;
temp->val = 1;
HASH_ADD(hh, *head, key, sizeof(long long), temp);
}
HashEntry *FindHash(HashEntry **head, long long key)
{
HashEntry *temp = NULL;
HASH_FIND(hh, *head, &key, sizeof(long long), temp);
return temp;
}
void InsertHash(HashEntry **head, long long key)
{
HashEntry *temp = NULL;
temp = FindHash(head, key);
if (temp) {
temp->val++;
} else {
AddHash(head, key);
}
}
void DelHash(HashEntry **head, long long key)
{
HashEntry *temp = NULL;
temp = FindHash(head, key);
if (temp) {
HASH_DEL(*head, temp);
free(temp);
}
}
void DivHash(HashEntry **head, long long key)
{
HashEntry *temp = NULL;
temp = FindHash(head, key);
if (temp) {
temp->val--;
}
}
bool IsLight(HashEntry **rows, HashEntry **cols, HashEntry **diagonal, HashEntry **antiDiaginal, int x, int y)
{
HashEntry *temp = NULL;
temp = FindHash(rows, x);
if (temp && temp->val > 0)
return true;
temp = FindHash(cols, y);
if (temp && temp->val > 0)
return true;
temp = FindHash(diagonal, x - y);
if (temp && temp->val > 0)
return true;
temp = FindHash(antiDiaginal, x + y);
if (temp && temp->val > 0)
return true;
return false;
}
bool IsIngrid(int n, int x, int y)
{
if (x >= 0 && x < n && y >= 0 && y < n) {
return true;
}
return false;
}
void CloseLight(HashEntry **points, HashEntry **rows, HashEntry **cols, HashEntry **diagonal, HashEntry **antiDiaginal,
int n, int x, int y)
{
int range[9][2] = {{0, 0}, {0, 1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {1, -1}, {1, 0}, {1, 1}};
for (int i = 0; i < 9; i++) {
if (IsIngrid(n, x + range[i][0], y + range[i][1]) == false) {
continue;
}
HashEntry *temp = NULL;
temp = FindHash(points, GetKey(x + range[i][0], y + range[i][1]));
if (temp == NULL)
continue;
DelHash(points, GetKey(x + range[i][0], y + range[i][1]));
DivHash(rows, (long long)(x + range[i][0]));
DivHash(cols, (long long)(y + range[i][1]));
DivHash(diagonal, (long long)(x + range[i][0] - (y + range[i][1])));
DivHash(antiDiaginal, (long long)(x + range[i][0] + y + range[i][1]));
}
}
int *gridIllumination(int n, int **lamps, int lampsSize, int *lampsColSize, int **queries, int queriesSize,
int *queriesColSize, int *returnSize)
{
int *ans = malloc(queriesSize * sizeof(int));
*returnSize = 0;
HashEntry *points = NULL;
HashEntry *rows = NULL, *cols = NULL;
HashEntry *diagonal = NULL, *antiDiaginal = NULL;
HashEntry *temp = NULL;
// 把灯放进去
for (int i = 0; i < lampsSize; i++) {
// 避免多次列出时, 值重复累加, 已经加过的值就不再加入
temp = FindHash(&points, ((long long)GetKey(lamps[i][0], lamps[i][1])));
if (temp) {
continue;
}
InsertHash(&points, (long long)GetKey(lamps[i][0], lamps[i][1]));
InsertHash(&rows, (long long)lamps[i][0]);
InsertHash(&cols, (long long)lamps[i][1]);
InsertHash(&diagonal, (long long)(lamps[i][0] - lamps[i][1]));
InsertHash(&antiDiaginal, (long long)(lamps[i][0] + lamps[i][1]));
}
for (int i = 0; i < queriesSize; i++) {
if (IsLight(&rows, &cols, &diagonal, &antiDiaginal, queries[i][0], queries[i][1])) {
ans[(*returnSize)++] = 1;
} else {
ans[(*returnSize)++] = 0;
}
// 关灯
CloseLight(&points, &rows, &cols, &diagonal, &antiDiaginal, n, queries[i][0], queries[i][1]);
}
return ans;
}
相关题目链接
1. 两数之和
884. 两句话中的不常见单词
1001. 网格照明
-----后面做了其他题目会陆续增加相关内容
—2020.02.08更新 使用 long long 型参数当key值 更新 1001 题解