1. 程式人生 > >雜湊查詢之鏈地址法解決衝突(程式碼封裝實現)

雜湊查詢之鏈地址法解決衝突(程式碼封裝實現)

鏈地址法的基本思想是:將所有雜湊地址為i 的元素構成一個稱為同義詞鏈的連結串列,並將連結串列的頭指標存在雜湊表的第i個單元中,因而查詢、插入和刪除主要在同義詞鏈中進行。 

該雜湊方法首先對關鍵碼集合用某一個雜湊函式計算它們的存放位置。

若設散列表地址空間的所有位置是從0到m-1,則關鍵碼集合中的所有關鍵碼被劃分為m個子集,具有相同地址的關鍵碼歸於同一子集。我們稱同一子集中的關鍵碼互為同義詞。每一個子集稱為一個桶。

通常各個桶中的表項通過一個連結串列連結起來,稱之為同義詞子表。所有桶號相同的表項都連結在同一個同義詞子表中,各連結串列的表頭結點組成一個向量。

進一步的分析:

1、通常,每個桶中的同義詞子表都很短,設有n個關鍵碼通過某一個雜湊函式,存放到散列表中的 m 個桶中。那麼每一個桶中的同

義詞子表的平均長度為 n / m。這樣,以搜尋平均長度為 n / m 的同義詞子表代替了搜尋長度為 n 的順序表,搜尋速度快得多(O(1))。

2、時間複雜度分析:應用鏈地址法處理溢位,需要增設連結指標,似乎增加了儲存開銷。事實上,由於開地址法必須保持大量的空閒空間以確保搜尋

效率,如二次探查法要求裝填因子(a = n / m)而表項所佔空間又比指標大得多,所以使用鏈地址法反而比開地址法節省存儲空間。

下面給出鏈地址法的實現,包括建立雜湊表,釋放雜湊表,在雜湊表中根據key查詢一項,根據key 插入一項,根據key 刪除一項等。

使用雙向連結串列實現。


hash.h
#ifndef _HASH_H_
#define _HASH_H_
//雙向連結串列的雜湊
typedef struct hash hash_t;
typedef unsigned int (*hashfunc_t)(unsigned int,void*);//雜湊函式

/*建立雜湊表*/
hash_t* hash_alloc(unsigned int buckets,hashfunc_t hash_func); 
/*查詢關鍵字*/
void* hash_lookup_entry(hash_t *hash,void* key,unsigned int key_size);
/*雜湊表中新增記錄*/
void hash_add_entry(hash_t *hash,void *key,unsigned int key_size,
        void *value,unsigned int value_size);
/*釋放雜湊表*/
void hash_free_entry(hash_t *hash,void *key,unsigned int key_size);

#endif

hash.c
#include "hash.h"
#include <assert.h>
#include "common.h"
typedef struct hash_node
{
    void *key; //任意型別的關鍵字
    void *value;//資訊
    struct hash_node *prev;//前驅指標
    struct hash_node *next;//後繼指標
}hash_node_t;

struct hash
{
    unsigned int buckets;
    hashfunc_t hash_func;
    hash_node_t **nodes;
};

//得到桶號
hash_node_t **hash_get_bucket(hash_t *hash,void *key)
{
    unsigned int bucket=hash->hash_func(hash->buckets,key);
    if(bucket >= hash->buckets)
    {
        fprintf(stderr,"bad buckets lookup\n");
        exit(EXIT_FAILURE);
    }

   return &(hash->nodes[bucket]);
}

hash_node_t *hash_get_node_by_key(hash_t *hash,void *key,unsigned int key_size)
{
    hash_node_t **bucket=hash_get_bucket(hash,key);
    hash_node_t *node=*bucket;
    if(node==NULL)
        return NULL;

    while(node!=NULL && memcmp(node->key,key,key_size)!=0)
    {
        node=node->next;
    }

    return node;
}

hash_t* hash_alloc(unsigned int buckets,hashfunc_t hash_func)
{
    hash_t *hash=(hash_t *)malloc(sizeof(hash_t));
    hash->buckets=buckets;
    hash->hash_func=hash_func;
    int size=buckets * sizeof(hash_node_t*);
    hash->nodes=(hash_node_t**)malloc(size);
    memset(hash->nodes,0,size);
    return hash;
     
}

void* hash_lookup_entry(hash_t *hash,void* key,unsigned int key_size)
{
    hash_node_t *node=hash_get_node_by_key(hash,key,key_size);
    if(node==NULL)
        return NULL;

    return node->value;
}


void hash_add_entry(hash_t *hash,void *key,unsigned int key_size,
        void *value,unsigned int value_size)
{
    //已經存在
    if(hash_lookup_entry(hash,key,key_size))
    {
        fprintf(stderr,"duplicate hash key\n");
        return ;
    }

    hash_node_t *node=malloc(sizeof(hash_node_t));
    node->prev=NULL;
    node->next=NULL;

    node->key=malloc(key_size);
    memcpy(node->key,key,key_size);

    node->value=malloc(value_size);
    memcpy(node->value,value,value_size);

    hash_node_t **bucket=hash_get_bucket(hash,key);
    if(*bucket == NULL)
    {
        *bucket=node;
    }
    else
    {
        //將新的節點插入到頭部
        node->next=*bucket;
        (*bucket)->prev=node;
        *bucket=node;
    }
}

//刪除操作
void hash_free_entry(hash_t *hash,void *key,unsigned int key_size)
{
     hash_node_t *node=hash_get_node_by_key(hash,key,key_size);
     if(node==NULL)
         return ;

     free(node->key);
     free(node->value);

     if(node->prev)
         node->prev->next=node->next;
     else
     {
         hash_node_t **bucket=hash_get_bucket(hash,key);
         *bucket=node->next;
     }

     if(node->next)
         node->next->prev=node->prev;
      
     free(node);
}


測試main.c
#include "hash.h"
#include "common.h"

typedef struct stu
{
   char sno[5];
   char name[32];
   int age;
}stu_t;

typedef struct stu2
{
   int sno;
   char name[32];
   int age;
}stu2_t;
//字串雜湊函式
unsigned int hash_str(unsigned int buckets,void *key)
{
    char *sno=(char *)key;
    unsigned int index=0;
    while(*sno)
    {
        index=*sno+4*index;
        sno++;
    }

    return index % buckets;
}

unsigned int hash_int(unsigned int buckets,void *key)
{
    int *sno=(int *)key;
    return (*sno) % buckets;
}

int main()
{
/*
    stu_t stu_arr[]=
    {
       {"1234","AAAA",20},
       {"4568","AAAA",23},
       {"6729","AAAA",19}
    };

    hash_t  *hash=hash_alloc(256,hash_str);

    int size=sizeof(stu_arr)/sizeof(stu_arr[0]);
    int i;
    for(i=0;i<size;i++)
    {//插入地址,關鍵碼,關鍵碼的長度,資料項
        hash_add_entry(hash,stu_arr[i].sno,strlen(stu_arr[i].sno),
                &stu_arr[i],sizeof(stu_arr[i]));
    }
    //查詢
    stu_t *s=(stu_t *)hash_lookup_entry(hash,"4568",strlen("4568"));
    if(s)
    {
        printf("%s %s %d\n",s->sno,s->name,s->age);
    }
    else
        printf("Not found\n");

    
    hash_free_entry(hash,"1234",strlen("1234"));

    s=(stu_t *)hash_lookup_entry(hash,"1234",strlen("1234"));
    if(s)
    {
        printf("%s %s %d\n",s->sno,s->name,s->age);
    }
    else
        printf("Not found\n");
*/

    stu2_t stu_arr[]=
    {
       {1234,"AAAA",20},
       {4568,"AAAA",23},
       {6729,"AAAA",19}
    };

    hash_t  *hash=hash_alloc(256,hash_int);

    int size=sizeof(stu_arr)/sizeof(stu_arr[0]);
    int i;
    for(i=0;i<size;i++)
    {//插入地址,關鍵碼,關鍵碼的長度,資料項
        hash_add_entry(hash,&(stu_arr[i].sno),sizeof(stu_arr[i].sno),
                &stu_arr[i],sizeof(stu_arr[i]));
    }
    //查詢
    int sno=4568;
    stu2_t *s=(stu2_t *)hash_lookup_entry(hash,&sno,sizeof(sno));
    if(s)
    {
        printf("%d %s %d\n",s->sno,s->name,s->age);
    }
    else
        printf("Not found\n");

    sno=1234;
    hash_free_entry(hash,&sno,sizeof(sno));

    s=(stu2_t *)hash_lookup_entry(hash,&sno,sizeof(sno));
    if(s)
    {
        printf("%d %s %d\n",s->sno,s->name,s->age);
    }
    else
        printf("Not found\n");
    return 0;
}

結果:

可以看到因為我們封裝的時候使用的是void *,所以支援各種型別的關鍵字,例如測試中的字串和整型。


相關推薦

查詢地址解決衝突程式碼封裝實現

鏈地址法的基本思想是:將所有雜湊地址為i 的元素構成一個稱為同義詞鏈的連結串列,並將連結串列的頭指標存在雜湊表的第i個單元中,因而查詢、插入和刪除主要在同義詞鏈中進行。  該雜湊方法首先對關鍵碼集合用某一個雜湊函式計算它們的存放位置。 若設散列表地址空間的所有位置是從

地址

鏈地址發     將所有的關鍵字為同義詞的記錄儲存在同一線性連結串列中。     比如,我們對於關鍵字集合{12,67,56,16,25,37, 22,29,15,47,48,34},我們用前面同樣的12為除數,進行除留餘數法: 當雜湊表中的

散列技術地址(基於無序表)

stc 分享 item size obj 單鏈表 結果 clu arc 源碼例如以下: #include <stdlib.h> #include <stdio.h> #define hash(v,M) (v % M) typedef c

CPP微軟表新增刪除方法_二級指標老師說的

//vs2017 #include #include “CMap.h” using namespace std; int main() { SStudent sstu[] = { {1,“李四”,95.5}, {2,“lisi”,98}, {3,“aslic”,98}, {1,“李思思

java 解決Hash()衝突的四種方法--開放定址(線性探測,二次探測,偽隨機探測)、地址、再、建立公共溢位區

一)雜湊表簡介 非雜湊表的特點:關鍵字在表中的位置和它之間不存在一個確定的關係,查詢的過程為給定值一次和各個關鍵字進行比較,查詢的效率取決於和給定值進行比較的次數。     雜湊表的特點:關鍵字在表中位置和它之間存在一種確定的關係。 雜湊函式:一般情況下,需要在

表——線性探測地址查詢成功、查詢不成功的平均長度

四、雜湊表的裝填因子 裝填因子 = (雜湊表中的記錄數) /  (雜湊表的長度) 裝填因子是雜湊表裝滿程度的標記因子。值越大,填入表中的資料元素越多,產生衝突的可能性越大。 五、不同處理衝突的平均查詢長度 例: 假設散列表的長度是13,三列函式為H(K) = k %

表-線性探測/地址

1.線性探測法 eg.假設散列表的長度是13,三列函式為H(K) = k % 13,給定的關鍵字序列為{32, 14, 23, 01, 42, 20, 45, 27, 55, 24, 10, 53}。分別畫出用線性探測法和拉鍊法解決衝突時構造的雜湊表,並求出在等概率情況下,這兩種方法的查詢成功和

散列表衝突處理的方法地址二次探測再實現

#include "hash.h"#include "common.h"#include <assert.h>typedef enum entry_status {     EMPTY,     ACTIVE,     DELETED } entry_status_t;typedef struct

表---開解決衝突

上篇文章已經寫過構造雜湊表時用開放定址法解決雜湊衝突,這次我就來寫寫這個開鏈法解決雜湊衝突的問題! 一、結點定義 我們已經知道開鏈法大概是一種什麼結構了,(如果不知道,這裡有講哦點我點我)顯而易

散列表衝突處理的方法地址線性探測再實現

    printf("The hash table has free.\n"); }void *hash_lookup_entry(hash_t *hash, void *key, unsigned int key_size) {     hash_node_t *node = hash_get_node_

表(HashTable)的開放定址地址實現

 散列表(Hash table,也叫雜湊表),是根據關鍵碼值(Key value)而直接進行訪問的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做雜湊函式,存放記錄的陣列叫做散列表。引用(百度) 演算法時間複雜度分析

資料結構查詢-查詢

目錄 雜湊法(計算式查詢) 雜湊函式的構造方法 處理衝突的方法 雜湊表的查詢過程 雜湊法(計算式查詢) 雜湊法又稱雜湊法、雜湊發、關鍵字地址計演算法,相應的表成為雜湊表、散列表等。 雜湊法的基本思想:首先在元素的關鍵字k和元素的儲存位置p之間建立一個對應

查詢演算法查詢近似O(1)的單點查詢方法

雜湊查詢是通過計算資料元素的儲存地址進行查詢的一種方法。O(1)的查詢,即所謂的秒殺。雜湊查詢的本質是先將資料對映成它的雜湊值。雜湊查詢的核心是構造一個雜湊函式,它將原來直觀、整潔的資料對映為看上去似乎是隨機的一些整數。 雜湊查詢的操作步驟: 1)       用給定的雜

構造二次探測

<pre name="code" class="cpp">//HashTable.h #pragma once #include<iostream> #include <string> using namespace std; enum State { EMPTY,//空

地址和開放定址,求等概率下查詢成功時的平均查詢長度

問題描述: 演算法與資料結構的一個題目,用鏈地址法和開放定址法,求等概率情況下查詢成功時的平均查詢長度 已知一組關鍵字(13,20,85,52,8),雜湊函式為:H(key)=key MOD 6

地址和線性探測查詢成功與不成功的平均查詢長度ASL

一、鏈地址法在等概率下查詢成功和查詢不成功的平均查詢長度: 將關鍵字序列{1 13 12 34 38 33 27 22} 雜湊儲存到散列表中。雜湊函式為:H(key)=key mod 11,處理衝突採用鏈地址法,求在等概率下查詢成功和查詢不成

查詢演算法查詢

雜湊查詢是通過計算資料元素的儲存地址進行查詢的一種方法。O(1)的查詢,即所謂的秒殺。雜湊查詢的本質是先將資料對映成它的雜湊值。雜湊查詢的核心是構造一個雜湊函式,它將原來直觀、整潔的資料對映為看上去似乎是隨機的一些整數。 雜湊查詢的操作步驟: 1)       用給定的雜湊

哈希,解決沖突

find clu 定義 前驅 else 元素 position not free #include<iostream>using namespace std; struct ListNode;typedef struct ListNode *Position;s

表——開放定址

一、Hash.h #ifndef __HASH_H__ #define __HASH_H__ #include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int HashDa

據說,80%的人都搞不懂演算法 區塊 演算法

本文約9000字+,閱讀(觀看)需要52分鐘 聊到區塊鏈的時候也少不了會聽到“雜湊”、“雜湊函式”、“雜湊演算法”,是不是聽得一頭霧水?別急,這一講我們來講講什麼是雜湊演算法。 雜湊是一種加密演算法 雜湊函式(Hash Function),也稱為雜湊函式或雜湊函式。雜湊函式是一個