初遇哈希表
一、哈希表
哈希表: 可以在 O(1) 的时间复杂度内找到元素
哈希函数: Hash(key) = key % capacity
哈希冲突: 不同的关键字计算出相同的哈希地址
这是哈希表无法解脱的桎梏,一旦使用哈希表,哈希冲突是无法避免的,只能通过巧妙的设计哈希函数,尽量减少哈希冲突,而无法避免
二、开放地址法解决哈希冲突
当发生哈希冲突时,就把值放到下一个空位
来看代码:点我查看/下载源码
// hash.h
#include <stdio.h>
#include <malloc.h>
#include <stdint.h>
#include <assert.h>
typedef int Key;
typedef enum State
{
EMPTY,
EXIST,
DELETE
}State;
typedef struct KeyType
{
Key key;
State state;
}KeyType;
typedef size_t(*HashFunc)(Key key, size_t capacity);
typedef struct HashTable{
KeyType *array;
size_t capacity;
size_t size;
HashFunc hashfunc;
}HashTable;
int HashInit(HashTable* pHT, size_t capacity, HashFunc hashfunc);
int HashDestory(HashTable *pHT);
int HashInsert(HashTable *pHT, Key key);
int HashSearch(const HashTable *pHT, Key key);
int HashRemove(HashTable *pHT, Key key);
//hash.c
#include "hash.h"
int HashInit(HashTable* pHT, size_t capacity, HashFunc hashfunc)
{
if (pHT == NULL)
{
return -1;
}
pHT->array = malloc(sizeof(KeyType)* capacity);
if (pHT->array == NULL)
{
return -1;
}
for (size_t i = 0; i < capacity; ++i)
{
pHT->array[i].state = EMPTY;
}
pHT->capacity = capacity;
pHT->size = 0;
pHT->hashfunc = hashfunc;
return 0;
}
int HashDestory(HashTable *pHT)
{
if (pHT == NULL)
{
return -1;
}
if (pHT->array == NULL)
{
return -1;
}
free(pHT->array);
pHT->array = NULL;
pHT->capacity = 0;
pHT->size = 0;
pHT->hashfunc = NULL;
return 0;
}
// 1. 计算哈希地址
// 2. 状态
int HashSearch(const HashTable *pHT, Key key)
{
if (pHT == NULL)
{
return -1;
}
// 计算哈希地址
size_t index = pHT->hashfunc(key, pHT->capacity);
size_t copy = index;
// 判断状态
while (1)
{
if (pHT->array[index].state == EXIST && pHT->array[index].key == key)
{
return index;
}
index = (index + 1) % pHT->capacity;
if (index == copy)
return -1;
}
}
// 检测容量
void checkCapacity(HashTable* pHT)
{
if (pHT->size * 100 / pHT->capacity < 75)
{// a 负载因子 不需要扩容
return;
}
HashTable temp;
HashInit(&temp, pHT->capacity * 2, pHT->hashfunc);
for (size_t i = 0; i < pHT->capacity; ++i)
{
HashInsert(&temp, pHT->array[i].key);
}
pHT->array = temp.array;
pHT->capacity = temp.capacity;
}
// 1. 先查找这个元素是否已经在哈希表中 在 返回 -1 插入失败
// 2. 不在哈希表中
// 1) 考虑是否要增容
// 2) 计算哈希地址 线性探测插入元素
int HashInsert(HashTable *pHT, Key key)
{
if (pHT == NULL)
{
return -1;
}
int res = HashSearch(pHT, key);
if (res != -1)
{
return -1;
}
checkCapacity(pHT);
int index = pHT->hashfunc(key, pHT->capacity);
while (1)
{// 已经检测容量了 所以肯定能插入成功
if (pHT->array[index].state == EMPTY)
{
pHT->array[index].key = key;
pHT->array[index].state = EXIST;
pHT->size++;
return index;
}
index = (index + 1) % pHT->capacity;
}
}
// 删除元素
int HashRemove(HashTable *pHT, Key key)
{
// 1. 先根据哈希函数 计算哈希地址
assert(pHT);
size_t index = pHT->hashfunc(key, pHT->capacity);
while (pHT->array[index].key != EMPTY)
{
if (pHT->array[index].state == EXIST && pHT->array[index].key == key)
{
pHT->array[index].state = DELETE;
pHT->size--;
return 0;
}
index = (index + 1) % pHT->capacity;
}
return -1;
}
//test.c
#include "hash.h"
#include <stdlib.h>
size_t hashFunc(Key key, size_t capacity)
{
return key % capacity;
}
void test()
{
HashTable ht;
HashInit(&ht, 7, hashFunc);
size_t index = HashInsert(&ht, 7);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 14);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 21);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 28);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 4);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 5);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 6);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashInsert(&ht, 9);
printf("ht.array[%d] = %d\n", index, ht.array[index]);
index = HashSearch(&ht, 9);
printf("index = %d\n", index);
index = HashRemove(&ht, 9);
if (index == -1)
{
printf("删除失败\n");
}
else
{
printf("删除成功\n");
}
index = HashSearch(&ht, 9);
printf("index = %d\n", index);
}
int main(void)
{
test();
system("pause");
return 0;
}
测试结果:
初遇哈希,如有不正,还请指出,谢谢 :)