1.個人理解
關於哈希表,通過看書與資料,有了一些自己的理解。
我更喜歡將哈希表比作是一個二維數組void chtbl[type][N]
同一數據類型的數據存儲在一個一維數組裏面,type表示不同類型的數據類型。
比如圓形,方形,心形,六邊形代表四種類型的框,如果一個物體數據某種類型,
則將該物體放入對應的框中。而N則代表相應數據類型的個數,
而如何辨別該物體是什麼形狀,則需要一套方法,也就是哈希函數。
下面所說的數據類型,並非是Int,double等基礎數據類型,而是具有相同規則的數據的稱呼。
例如:取餘後,餘數相同的數據。最後一位是都是A的數據等等
2.解決衝突
哈希表存在一個衝突問題,比如上圖,只有長方形或圓形持續增多的情況下,如果要搜索圓形或長方形,那麼也是要花費的時間也會越來越長。
理想情況下,所以類型的數據,應該是同步增長的。也就是說儘可能的將數據隨機分佈到各個類型中去,也就是均勻分佈。但實際上只能是儘可能達到。
如果想插入表中的數據數量遠遠大於表中數據類型的數量,那麼及時是均勻分佈的情況,表的性能也會大大降低。這種情況下,每個數據類型下的數據量會越來越大。哈希表中將數據n,和數據類型m的商稱爲,負載因子。
即:a=n/m
例如:一個哈希表的數據量爲1000,數據類型爲100種,那麼負載因子就是10.這樣1000個數據,查找數據的最壞情況,也不過是查找10次,就必定能查找到。
3.代碼實現
//鏈式哈希表的實現
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct ListElmt //定義節點
{
void *data;
struct ListElmt *next;
};
typedef struct List //定義鏈表
{
int size;
int(*match)(const void *key1, const void *key2); //構造函數
void(*destroy)(void *data); //析構函數
ListElmt *head;
ListElmt *tail;
};
//新建鏈表
void list_init(List *list, void(*destroy)(void *data))
{
list->size = 0;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
}
void list_destroy(List *haed);
typedef struct CHTBL_
{
int buckets; //數據類型的個數
int (*h)(const void *key); //自定義的哈希函數
int (*match)(const void *key1, const void *key2); //判斷兩個鍵是否匹配
void (*destroy)(void *data); //釋放動態分配的內存
int size; //數據數量
List *table; //存儲數據類型的數組
}CHTBL;
void chtbl_destroy(CHTBL *htbl)
{
int i;
for (i = 0; i < htbl->buckets; i++)
{
list_destory(&htbl->table[i]);
}
free(htbl->table);
memset(htbl, 0, sizeof(CHTBL));
return;
}
//哈希表初始化
int chtbl_init(CHTBL *htbl, int buckets, int(*h)(const void *key),
int(*match)(const void *key1, const void *key2), void(*destroy)(void *data))
{
int i;
if ((htbl->table == (List *)malloc(sizeof(buckets * sizeof(List)))) == NULL)
{
return -1;
}
htbl->buckets = buckets;
//初始化哈希表數據類型
for (i = 0; i < htbl->buckets; i++)
{
list_init(&htbl->table[i], destroy);
}
htbl->h = h;
htbl->match = match;
htbl->destroy = destroy;
htbl->size = 0;
return 0;
}
//銷燬哈希表
void chtbl_destroy(CHTBL *htbl)
{
int i;
for (i = 0; i < htbl->buckets; i++)
{
list_destroy(&htbl->table[i]); //鏈表銷燬函數
}
memset(htbl, 0, sizeof(CHTBL));
return;
}
//查找元素,找到返回0,否則,返回-1
int chtbl_lookup(const CHTBL *htbl, void **data)
{
ListElmt *element;
int bucket;
//直接對數據類型數取餘,得到key
bucket = htbl->h(*data) % htbl->buckets;
//從獲得數據類型的一維數組開始尋找data是否存在
for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element))
{
if (htbl->match(*data, list_data(element)))
{
*data = list_data(element);
return 0;
}
}
return -1;
}
//插入數據
int chtbl_insert(CHTBL *htbl, const void *data)
{
void *temp;
int bucket, retval;
temp = (void *)data;
//如果數據存在,則返回1,不進行插入操作
if (chtbl_lookup(htbl,&temp) == 0)
return 1;
//獲取傳入數據的key(數據類型)
bucket = htbl->h(data) % htbl->buckets;
//如果數據不存在,將這個數據連接在這個數據類型的鏈表上
if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
htbl->size++;
return retval;
}
int chtbl_remove(CHTBL *htbl, void **data)
{
ListElmt *element,*prev;
int bucket;
bucket = htbl->h(data) % htbl->buckets;
prev = NULL;
//根據key尋找需要刪除的數據
for (element = list_head(&htbl->table[bucket]);
element != NULL; element = list_next(element))
{
//如果找到數據
if (htbl->match(*data, list_data(element)))
{
//開始刪除,刪除成功返回0,刪除失敗返回-1
if (list_rem_next(&htbl->table[bucket], prev, data) == 0)
{
htbl->size--;
return 0;
}
else
{
return -1;
}
}
prev = element;
}
return -1;
}