Hash算法中要解決一個碰撞沖突的辦法,后文中描述了幾種解決方法。下面代碼中用的是鏈式地址法,就是用鏈表和數組實現HASH表。
he/*hash table max size*/ #define HASH_TABLE_MAX_SIZE 40 /*hash table大小*/ int hash_table_size=0; /*.BH----------------------------------------------------------------- ** 結構體定義 **.EH----------------------------------------------------------------- */ /*hashTable結構*/ typedef int HashKeyType; typedef struct{ OMS_TYPE__CurrFaultReport curr_fault_report; unsigned int begin_time[SYS_FAULT_REPORT_MAX_NUM]; unsigned int end_time[SYS_FAULT_REPORT_MAX_NUM]; unsigned int report_valid[SYS_FAULT_REPORT_MAX_NUM]; }HashValueType; typedef struct HashNode_Struct HashNode; struct HashNode_Struct { HashKeyType sKey; HashValueType nValue; HashNode* pNext; }; HashNode* hashTable[HASH_TABLE_MAX_SIZE]; //hash table data strcutrue /*=================hash table function======================*/ /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:string hash function ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ unsigned int hash_table_hash_str(const char* skey) { const signed char *p = (const signed char*)skey; unsigned int h = *p; if(h) { for(p += 1; *p != '\0'; ++p){ h = (h << 5) - h + *p; } } return h; } /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:insert key-value into hash table ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ int hash_table_insert(const HashKeyType skey, HashValueType nvalue) { unsigned int pos = 0; HashNode* pHead = NULL; HashNode* pNewNode = NULL; if (hash_table_size >= HASH_TABLE_MAX_SIZE) { printf("out of hash table memory!\n"); return 0; } pos = hash_table_hash_str(skey) % HASH_TABLE_MAX_SIZE; pHead = hashTable[pos]; while (pHead) { if (pHead->sKey == skey) { printf("hash_table_insert: key %d already exists!\n", skey); return 0; } pHead = pHead->pNext; } pNewNode = (HashNode*)malloc(sizeof(HashNode)); memset(pNewNode, 0, sizeof(HashNode)); pNewNode->sKey = skey; memcpy(&pNewNode->nValue, &nvalue, sizeof(HashValueType)); pNewNode->pNext = hashTable[pos]; hashTable[pos] = pNewNode; hash_table_size++; return 1; } /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:lookup a key in the hash table ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ HashNode* hash_table_find(const HashKeyType skey) { unsigned int pos = 0; pos = hash_table_hash_str(skey) % HASH_TABLE_MAX_SIZE; if (hashTable[pos]) { HashNode* pHead = hashTable[pos]; while (pHead) { if (skey == pHead->sKey) return pHead; pHead = pHead->pNext; } } return NULL; } /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:free the memory of the hash table ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ void hash_table_release() { int i; for (i = 0; i < HASH_TABLE_MAX_SIZE; ++i) { if (hashTable[i]) { HashNode* pHead = hashTable[i]; while (pHead) { HashNode* pTemp = pHead; pHead = pHead->pNext; if (pTemp) { free(pTemp); } } } } } //remove key-value frome the hash table /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:string hash function ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ void hash_table_remove(const HashKeyType skey) { unsigned int pos = hash_table_hash_str(skey) % HASH_TABLE_MAX_SIZE; if (hashTable[pos]) { HashNode* pHead = hashTable[pos]; HashNode* pLast = NULL; HashNode* pRemove = NULL; while (pHead) { if (skey == pHead->sKey) { pRemove = pHead; break; } pLast = pHead; pHead = pHead->pNext; } if (pRemove) { if (pLast) pLast->pNext = pRemove->pNext; else hashTable[pos] = NULL; free(pRemove); } } hash_table_size--; } /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:print the content in the hash table ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ void hash_table_print() { int i; printf("===========content of hash table===========\n"); for (i = 0; i < HASH_TABLE_MAX_SIZE; ++i){ if (hashTable[i]) { HashNode* pHead = hashTable[i]; printf("%d=>", i); while (pHead) { printf("%d:%d ", pHead->sKey, pHead->nValue.begin_time); pHead = pHead->pNext; } printf("\n"); } } } /*.BH----------------------------------------------------------------- ** **函數名: ** **功能:初始化系統名稱的hashTable,插入所有系統名稱 ** **參數: 無 ** **返回值:無 ** **設計注記: ** **.EH----------------------------------------------------------------- */ void Common_InitHashTable() { hash_table_size = 0; memset(hashTable, 0, sizeof(HashNode*) * HASH_TABLE_MAX_SIZE); }
Hash碰撞沖突
Hash函數的作用就是保證對象返回唯一hash值,但當兩個對象計算值一樣時,這就發生了碰撞沖突。如下將介紹如何處理沖突,當然其前提是一致性hash。
1.開放地址法
開放地執法有一個公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)
其中,m為哈希表的表長。di 是產生沖突的時候的增量序列。如果di值可能為1,2,3,…m-1,稱線性探測再散列。
如果di取1,則每次沖突之后,向后移動1個位置.如果di取值可能為1,-1,2,-2,4,-4,9,-9,16,-16,…k*k,-k*k(k<=m/2),稱二次探測再散列。
如果di取值可能為偽隨機數列。稱偽隨機探測再散列。
2.再哈希法
當發生沖突時,使用第二個、第三個、哈希函數計算地址,直到無沖突時。缺點:計算時間增加。
比如上面第一次按照姓首字母進行哈希,如果產生沖突可以按照姓字母首字母第二位進行哈希,再沖突,第三位,直到不沖突為止
3.鏈地址法(拉鏈法)
將所有關鍵字為同義詞的記錄存儲在同一線性鏈表中。如下:

因此這種方法,可以近似的認為是筒子里面套筒子
4.建立一個公共溢出區
假設哈希函數的值域為[0,m-1],則設向量HashTable[0..m-1]為基本表,另外設立存儲空間向量OverTable[0..v]用以存儲發生沖突的記錄。
優缺點:
優點:
①拉鏈法處理沖突簡單,且無堆積現象,即非同義詞決不會發生沖突,因此平均查找長度較短;
②由於拉鏈法中各鏈表上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況;
③開放定址法為減少沖突,要求裝填因子α較小,故當結點規模較大時會浪費很多空間。而拉鏈法中可取α≥1,且結點較大時,拉鏈法中增加的指針域可忽略不計,因此節省空間;
④在用拉鏈法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去鏈表上相應的結點即可。而對開放地址法構造的散列表,刪除結點不能簡單地將被刪結 點的空間置為空,否則將截斷在它之后填人散列表的同義詞結點的查找路徑。這是因為各種開放地址法中,空地址單元(即開放地址)都是查找失敗的條件。因此在 用開放地址法處理沖突的散列表上執行刪除操作,只能在被刪結點上做刪除標記,而不能真正刪除結點。
缺點:
指針需要額外的空間,故當結點規模較小時,開放定址法較為節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的沖突,從而提高平均查找速度。
開放地址法和拉鏈法是比較常用的兩種,各有優缺點,開放地址法的過程可以參考以下鏈接。
參考鏈接:HASH碰撞
