用鏈表和數組實現HASH表,幾種碰撞沖突解決方法


 

 

  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碰撞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM