什麼是哈希搜索結構
順序搜索以及二叉搜索樹中,元素存儲位置和元素各關鍵碼之間沒有對應的關係,因此在查找一個元素的時候,必須要經過關鍵碼的多次比較。所以這樣的搜索效率取決於搜索過程中元素的比較次數。
而我們理想的搜索方法就是不用經過任何比較,一次直接從表中得到要搜索的元素。如果能夠構造一種結構,讓所要查找的關鍵碼與它的存儲位置有一定的關係,那麼可以根據這種關係找到其位置從而更快的找到關鍵碼。
在這種結構中插入時,根據待插入的元素從而經過某些函數計算出它的插入位置,進行插入;查找時通過對查找元素的計算出其位置,再根據位置在結構中的對應出進行比較,如果相等則查找成功。
這種方式的結構就是哈希搜索結構,其中使用的計算位置函數就是哈希函數。
哈希衝突
在說哈希衝突之前,首先我們先定義一個簡單的哈希函數方便我們解釋。
int HashFun(int key)
{
return key % 1000;//關鍵碼爲key,其存儲對應位置爲key模1000
}
這時候我們就可以建立起關鍵碼與其存儲位置之間的關係了。
這個時候的1001應該插入到什麼位置呢?這種情況的出現就是哈希衝突。
解決哈希衝突常見的有兩種方法:閉散列和開散列。
負載因子
爲了增加哈希表的搜索效率,儘量不讓哈希表插滿,而是規定一個負載因子,控制哈希表內插入元素的個數,從而提高效率。
負載因子的定義爲:α = 填入表中元素 / 可填入總元素
α 越大,表明填入表內元素越多,產生哈希衝突的可能就越大。相反則越小。
閉散列
閉散列解決哈希衝突的方式是,當發生哈希衝突時,如果哈希表未放滿,則哈希表內必定有空餘位置,那麼此時將衝突的關鍵碼放置表中的下一個空位去。
接下來來,我們實現一下閉散列哈希表的基本操作。
實現
//hash.h
#pragma once
#include <stddef.h>
#define HASHMAXSIZE 1000//總個數
typedef int Keytype;
typedef int Valtype;
typedef size_t (*HashFun)(Keytype key);
typedef enum Stat {
Empty,//空
Valid,//有效
Delete//已刪除
} Stat;//標誌位
typedef struct HashElem {
Keytype key;//關鍵碼
Valtype val;//data
Stat stat;
} HashElem;
typedef struct HashTable {
HashElem data[HASHMAXSIZE];
size_t size;//元素個數
HashFun fun;//哈希函數
} HashTable;
void HashInit(HashTable* ht, HashFun fun);//初始化哈希表
void HashInsert(HashTable* ht, Keytype key, Valtype val);//插入哈希表
void HashDestroy(HashTable* ht);//銷燬哈希表
void HashRemove(HashTable* ht, Keytype key);//刪除哈希表指定元素
int HashFind(HashTable* ht, Keytype key, Valtype* val);//查找哈希表內元素
//hash.c
#include "hash.h"
#include <stdio.h>
size_t Function(Keytype key) {
return key % HASHMAXSIZE;
}
void HashInit(HashTable* ht, HashFun fun)//初始化哈希表
{
if(ht == NULL) {
return;
}
ht->size = 0;
ht->fun = fun;
size_t i = 0;
for(; i < HASHMAXSIZE; ++i) {
ht->data[i].stat = Empty;
}
return;
}
void HashInsert(HashTable* ht, Keytype key, Valtype val)//插入哈希表
{
if(ht == NULL) {
return;
}
if(ht->size > 0.8 * HASHMAXSIZE) {//負載因子
return;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].key == key && ht->data[offset].stat == Valid) {
//如果關鍵值key相同,那麼就插入失敗
return;
}
if(ht->data[offset].stat != Valid) {
ht->data[offset].key = key;
ht->data[offset].val = val;
ht->data[offset].stat = Valid;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
++ht->size;
return;
}
void HashDestroy(HashTable* ht)//銷燬哈希表
{
if(ht == NULL) {
return;
}
ht->size = 0;
ht->fun = NULL;
size_t i = 0;
for(; i < HASHMAXSIZE; ++i) {
ht->data[i].stat = Empty;
}
return;
}
void HashRemove(HashTable* ht, Keytype key)//刪除哈希表指定元素
{
if(ht == NULL) {
return;
}
if(ht->size == 0) {
return;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].stat != Valid) {
break;
}
if(ht->data[offset].key == key) {
ht->data[offset].stat = Delete;//改變狀態
--ht->size;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
return;
}
int HashFind(HashTable* ht, Keytype key, Valtype* val)//查找哈希表內元素
{
if(ht == NULL) {
return 0;
}
if(ht->size == 0) {
return 0;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].stat != Valid) {
return 0;
}
if(ht->data[offset].key == key) {
ht->data[offset].stat = Delete;
--ht->size;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
*val = ht->data[offset].val;
return 1;
}
歡迎大家共同討論,如有錯誤及時聯繫作者指出,並改正。謝謝大家!